diff options
-rw-r--r-- | net/dns/dns_protocol.h | 2 | ||||
-rw-r--r-- | net/dns/record_parsed.cc | 58 | ||||
-rw-r--r-- | net/dns/record_parsed.h | 54 | ||||
-rw-r--r-- | net/dns/record_parsed_unittest.cc | 35 | ||||
-rw-r--r-- | net/dns/record_rdata.cc | 131 | ||||
-rw-r--r-- | net/dns/record_rdata.h | 149 | ||||
-rw-r--r-- | net/dns/record_rdata_unittest.cc | 146 | ||||
-rw-r--r-- | net/net.gyp | 21 |
8 files changed, 596 insertions, 0 deletions
diff --git a/net/dns/dns_protocol.h b/net/dns/dns_protocol.h index bbd8fa2..99563b5 100644 --- a/net/dns/dns_protocol.h +++ b/net/dns/dns_protocol.h @@ -103,8 +103,10 @@ static const uint16 kClassIN = 1; // http://www.iana.org/assignments/dns-parameters static const uint16 kTypeA = 1; static const uint16 kTypeCNAME = 5; +static const uint16 kTypePTR = 12; static const uint16 kTypeTXT = 16; static const uint16 kTypeAAAA = 28; +static const uint16 kTypeSRV = 33; // DNS rcode values. static const uint8 kRcodeMask = 0xf; diff --git a/net/dns/record_parsed.cc b/net/dns/record_parsed.cc new file mode 100644 index 0000000..f02fbe3 --- /dev/null +++ b/net/dns/record_parsed.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2013 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/dns/record_parsed.h" + +#include "net/dns/dns_response.h" +#include "net/dns/record_rdata.h" + +namespace net { + +RecordParsed::RecordParsed(const std::string& name, uint16 type, uint16 klass, + uint32 ttl, scoped_ptr<const RecordRdata> rdata) + : name_(name), type_(type), klass_(klass), ttl_(ttl), rdata_(rdata.Pass()) { +} + +RecordParsed::~RecordParsed() { +} + +// static +scoped_ptr<const RecordParsed> RecordParsed::CreateFrom( + DnsRecordParser* parser) { + DnsResourceRecord record; + scoped_ptr<const RecordRdata> rdata; + + if (!parser->ReadRecord(&record)) + return scoped_ptr<const RecordParsed>(); + + switch (record.type) { + case ARecordRdata::kType: + rdata = ARecordRdata::Create(record.rdata, *parser); + break; + case CnameRecordRdata::kType: + rdata = CnameRecordRdata::Create(record.rdata, *parser); + break; + case PtrRecordRdata::kType: + rdata = PtrRecordRdata::Create(record.rdata, *parser); + break; + case SrvRecordRdata::kType: + rdata = SrvRecordRdata::Create(record.rdata, *parser); + break; + case TxtRecordRdata::kType: + rdata = TxtRecordRdata::Create(record.rdata, *parser); + break; + default: + return scoped_ptr<const RecordParsed>(); + } + + if (!rdata.get()) + return scoped_ptr<const RecordParsed>(); + + return scoped_ptr<const RecordParsed>(new RecordParsed(record.name, + record.type, + record.klass, + record.ttl, + rdata.Pass())); +} +} diff --git a/net/dns/record_parsed.h b/net/dns/record_parsed.h new file mode 100644 index 0000000..da58b26 --- /dev/null +++ b/net/dns/record_parsed.h @@ -0,0 +1,54 @@ +// Copyright (c) 2013 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_DNS_RECORD_PARSED_H_ +#define NET_DNS_RECORD_PARSED_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "net/base/net_export.h" + +namespace net { + +class DnsRecordParser; +class RecordRdata; + +// Parsed record. This is a form of DnsResourceRecord where the rdata section +// has been parsed into a data structure. +class NET_EXPORT_PRIVATE RecordParsed { + public: + virtual ~RecordParsed(); + + // All records are inherently immutable. Return a const pointer. + static scoped_ptr<const RecordParsed> CreateFrom(DnsRecordParser* parser); + + const std::string& name() const { return name_; } + uint16 type() const { return type_; } + uint16 klass() const { return klass_; } + uint32 ttl() const { return ttl_; } + + template <class T> const T* rdata() const { + if (T::kType != type_) + return NULL; + return static_cast<const T*>(rdata_.get()); + } + + private: + RecordParsed(const std::string& name, uint16 type, uint16 klass, uint32 ttl, + scoped_ptr<const RecordRdata> rdata); + + std::string name_; // in dotted form + const uint16 type_; + const uint16 klass_; + const uint32 ttl_; + + const scoped_ptr<const RecordRdata> rdata_; + + DISALLOW_COPY_AND_ASSIGN(RecordParsed); +}; + +} // namespace net + +#endif // NET_DNS_RECORD_PARSED_H_ diff --git a/net/dns/record_parsed_unittest.cc b/net/dns/record_parsed_unittest.cc new file mode 100644 index 0000000..bb82b53 --- /dev/null +++ b/net/dns/record_parsed_unittest.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2013 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/dns/record_parsed.h" + +#include "net/dns/dns_protocol.h" +#include "net/dns/dns_response.h" +#include "net/dns/dns_test_util.h" +#include "net/dns/record_rdata.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(RecordParsedTest, ParseSingleRecord) { + DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram), + sizeof(dns_protocol::Header)); + scoped_ptr<const RecordParsed> record; + const CnameRecordRdata* rdata; + + parser.SkipQuestion(); + record = RecordParsed::CreateFrom(&parser); + EXPECT_TRUE(record != NULL); + + ASSERT_EQ("codereview.chromium.org", record->name()); + ASSERT_EQ(dns_protocol::kTypeCNAME, record->type()); + ASSERT_EQ(dns_protocol::kClassIN, record->klass()); + + rdata = record->rdata<CnameRecordRdata>(); + ASSERT_TRUE(rdata != NULL); + ASSERT_EQ(kT1CanonName, rdata->cname()); + + ASSERT_FALSE(record->rdata<SrvRecordRdata>()); +} +} diff --git a/net/dns/record_rdata.cc b/net/dns/record_rdata.cc new file mode 100644 index 0000000..1de3a4c --- /dev/null +++ b/net/dns/record_rdata.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2013 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/dns/record_rdata.h" + +#include "net/base/big_endian.h" +#include "net/base/dns_util.h" +#include "net/dns/dns_protocol.h" +#include "net/dns/dns_response.h" + +namespace net { + +static const size_t kSrvRecordMinimumSize = 6; + +RecordRdata::RecordRdata() { +} + +SrvRecordRdata::SrvRecordRdata() : priority_(0), weight_(0), port_(0) { +} + +SrvRecordRdata::~SrvRecordRdata() {} + +// static +scoped_ptr<SrvRecordRdata> SrvRecordRdata::Create( + const base::StringPiece& data, + const DnsRecordParser& parser) { + if (data.size() < kSrvRecordMinimumSize) return scoped_ptr<SrvRecordRdata>(); + + scoped_ptr<SrvRecordRdata> rdata(new SrvRecordRdata); + + BigEndianReader reader(data.data(), data.size()); + // 2 bytes for priority, 2 bytes for weight, 2 bytes for port. + reader.ReadU16(&rdata->priority_); + reader.ReadU16(&rdata->weight_); + reader.ReadU16(&rdata->port_); + + if (!parser.ReadName(data.substr(kSrvRecordMinimumSize).begin(), + &rdata->target_)) + return scoped_ptr<SrvRecordRdata>(); + + return rdata.Pass(); +} + +ARecordRdata::ARecordRdata() { +} + +ARecordRdata::~ARecordRdata() { +} + +// static +scoped_ptr<ARecordRdata> ARecordRdata::Create( + const base::StringPiece& data, + const DnsRecordParser& parser) { + if (data.size() != kIPv4AddressSize) + return scoped_ptr<ARecordRdata>(); + + scoped_ptr<ARecordRdata> rdata(new ARecordRdata); + + rdata->address_.resize(kIPv4AddressSize); + for (unsigned i = 0; i < kIPv4AddressSize; ++i) { + rdata->address_[i] = data[i]; + } + + return rdata.Pass(); +} + +CnameRecordRdata::CnameRecordRdata() { +} + +CnameRecordRdata::~CnameRecordRdata() { +} + +// static +scoped_ptr<CnameRecordRdata> CnameRecordRdata::Create( + const base::StringPiece& data, + const DnsRecordParser& parser) { + scoped_ptr<CnameRecordRdata> rdata(new CnameRecordRdata); + + if (!parser.ReadName(data.begin(), &rdata->cname_)) + return scoped_ptr<CnameRecordRdata>(); + + return rdata.Pass(); +} + +PtrRecordRdata::PtrRecordRdata() { +} + +PtrRecordRdata::~PtrRecordRdata() { +} + +// static +scoped_ptr<PtrRecordRdata> PtrRecordRdata::Create( + const base::StringPiece& data, + const DnsRecordParser& parser) { + scoped_ptr<PtrRecordRdata> rdata(new PtrRecordRdata); + + if (!parser.ReadName(data.begin(), &rdata->ptrdomain_)) + return scoped_ptr<PtrRecordRdata>(); + + return rdata.Pass(); +} + +TxtRecordRdata::TxtRecordRdata() { +} + +TxtRecordRdata::~TxtRecordRdata() { +} + +// static +scoped_ptr<TxtRecordRdata> TxtRecordRdata::Create( + const base::StringPiece& data, + const DnsRecordParser& parser) { + scoped_ptr<TxtRecordRdata> rdata(new TxtRecordRdata); + + for (size_t i = 0; i < data.size(); ) { + uint8 length = data[i]; + + if (i + length >= data.size()) + return scoped_ptr<TxtRecordRdata>(); + + rdata->texts_.push_back(data.substr(i + 1, length).as_string()); + + // Move to the next string. + i += length + 1; + } + + return rdata.Pass(); +} + +} // namespace net diff --git a/net/dns/record_rdata.h b/net/dns/record_rdata.h new file mode 100644 index 0000000..76e9b37 --- /dev/null +++ b/net/dns/record_rdata.h @@ -0,0 +1,149 @@ +// Copyright (c) 2013 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_DNS_RECORD_RDATA_H_ +#define NET_DNS_RECORD_RDATA_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/base/big_endian.h" +#include "net/base/net_export.h" +#include "net/base/net_util.h" +#include "net/dns/dns_protocol.h" + +namespace net { + +class DnsRecordParser; + +// Parsed represenation of the extra data in a record. Does not include standard +// DNS record data such as TTL, Name, Type and Class. +class NET_EXPORT_PRIVATE RecordRdata { + public: + virtual ~RecordRdata() {} + + protected: + RecordRdata(); + + DISALLOW_COPY_AND_ASSIGN(RecordRdata); +}; + +// SRV record format (http://www.ietf.org/rfc/rfc2782.txt): +// 2 bytes network-order unsigned priority +// 2 bytes network-order unsigned weight +// 2 bytes network-order unsigned port +// target: domain name (on-the-wire representation) +class NET_EXPORT_PRIVATE SrvRecordRdata : public RecordRdata { + public: + static const uint16 kType = dns_protocol::kTypeSRV; + + virtual ~SrvRecordRdata(); + static scoped_ptr<SrvRecordRdata> Create(const base::StringPiece& data, + const DnsRecordParser& parser); + + uint16 priority() const { return priority_; } + uint16 weight() const { return weight_; } + uint16 port() const { return port_; } + + const std::string& target() const { return target_; } + + private: + SrvRecordRdata(); + + uint16 priority_; + uint16 weight_; + uint16 port_; + + std::string target_; + + DISALLOW_COPY_AND_ASSIGN(SrvRecordRdata); +}; + +// A Record format (http://www.ietf.org/rfc/rfc1035.txt): +// 4 bytes for IP address. +class NET_EXPORT_PRIVATE ARecordRdata : public RecordRdata { + public: + static const uint16 kType = dns_protocol::kTypeA; + + virtual ~ARecordRdata(); + static scoped_ptr<ARecordRdata> Create(const base::StringPiece& data, + const DnsRecordParser& parser); + + const IPAddressNumber& address() const { return address_; } + + private: + ARecordRdata(); + + IPAddressNumber address_; + + DISALLOW_COPY_AND_ASSIGN(ARecordRdata); +}; + +// CNAME record format (http://www.ietf.org/rfc/rfc1035.txt): +// cname: On the wire representation of domain name. +class NET_EXPORT_PRIVATE CnameRecordRdata : public RecordRdata { + public: + static const uint16 kType = dns_protocol::kTypeCNAME; + + virtual ~CnameRecordRdata(); + static scoped_ptr<CnameRecordRdata> Create(const base::StringPiece& data, + const DnsRecordParser& parser); + + std::string cname() const { return cname_; } + + private: + CnameRecordRdata(); + + std::string cname_; + + DISALLOW_COPY_AND_ASSIGN(CnameRecordRdata); +}; + +// PTR record format (http://www.ietf.org/rfc/rfc1035.txt): +// domain: On the wire representation of domain name. +class NET_EXPORT_PRIVATE PtrRecordRdata : public RecordRdata { + public: + static const uint16 kType = dns_protocol::kTypePTR; + + virtual ~PtrRecordRdata(); + static scoped_ptr<PtrRecordRdata> Create(const base::StringPiece& data, + const DnsRecordParser& parser); + + std::string ptrdomain() const { return ptrdomain_; } + + private: + PtrRecordRdata(); + + std::string ptrdomain_; + + DISALLOW_COPY_AND_ASSIGN(PtrRecordRdata); +}; + +// TXT record format (http://www.ietf.org/rfc/rfc1035.txt): +// texts: One or more <character-string>s. +// a <character-string> is a length octet followed by as many characters. +class NET_EXPORT_PRIVATE TxtRecordRdata : public RecordRdata { + public: + static const uint16 kType = dns_protocol::kTypeTXT; + + virtual ~TxtRecordRdata(); + static scoped_ptr<TxtRecordRdata> Create(const base::StringPiece& data, + const DnsRecordParser& parser); + + const std::vector<std::string>& texts() const { return texts_; } + + private: + TxtRecordRdata(); + + std::vector<std::string> texts_; + + DISALLOW_COPY_AND_ASSIGN(TxtRecordRdata); +}; +} + +#endif // NET_DNS_RECORD_RDATA_H_ diff --git a/net/dns/record_rdata_unittest.cc b/net/dns/record_rdata_unittest.cc new file mode 100644 index 0000000..c046b6f --- /dev/null +++ b/net/dns/record_rdata_unittest.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2013 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 "base/memory/scoped_ptr.h" +#include "net/dns/record_rdata.h" +#include "net/dns/dns_response.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(RecordRdataTest, ParseSrvRecord) { + scoped_ptr<SrvRecordRdata> record1_obj; + scoped_ptr<SrvRecordRdata> record2_obj; + + // These are just the rdata portions of the DNS records, rather than complete + // records, but it works well enough for this test. + + const char record[] = { + 0x00, 0x01, + 0x00, 0x02, + 0x00, 0x50, + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x01, 0x01, + 0x01, 0x02, + 0x01, 0x03, + 0x04, 'w', 'w', 'w', '2', + 0xc0, 0x0a, // Pointer to "google.com" + }; + + DnsRecordParser parser(record, sizeof(record), 0); + const unsigned first_record_len = 22; + base::StringPiece record1_strpiece(record, first_record_len); + base::StringPiece record2_strpiece( + record + first_record_len, sizeof(record) - first_record_len); + + record1_obj = SrvRecordRdata::Create(record1_strpiece, parser); + ASSERT_TRUE(record1_obj != NULL); + ASSERT_EQ(1, record1_obj->priority()); + ASSERT_EQ(2, record1_obj->weight()); + ASSERT_EQ(80, record1_obj->port()); + + ASSERT_EQ("www.google.com", record1_obj->target()); + + record2_obj = SrvRecordRdata::Create(record2_strpiece, parser); + ASSERT_TRUE(record2_obj != NULL); + ASSERT_EQ(257, record2_obj->priority()); + ASSERT_EQ(258, record2_obj->weight()); + ASSERT_EQ(259, record2_obj->port()); + + ASSERT_EQ("www2.google.com", record2_obj->target()); +} + +TEST(RecordRdataTest, ParseARecord) { + scoped_ptr<ARecordRdata> record_obj; + + // These are just the rdata portions of the DNS records, rather than complete + // records, but it works well enough for this test. + + const char record[] = { + 0x7F, 0x00, 0x00, 0x01 // 127.0.0.1 + }; + + DnsRecordParser parser(record, sizeof(record), 0); + base::StringPiece record_strpiece(record, sizeof(record)); + + record_obj = ARecordRdata::Create(record_strpiece, parser); + ASSERT_TRUE(record_obj != NULL); + + ASSERT_EQ("127.0.0.1", IPAddressToString(record_obj->address())); +} + +TEST(RecordRdataTest, ParseCnameRecord) { + scoped_ptr<CnameRecordRdata> record_obj; + + // These are just the rdata portions of the DNS records, rather than complete + // records, but it works well enough for this test. + + const char record[] = { + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00 + }; + + DnsRecordParser parser(record, sizeof(record), 0); + base::StringPiece record_strpiece(record, sizeof(record)); + + record_obj = CnameRecordRdata::Create(record_strpiece, parser); + ASSERT_TRUE(record_obj != NULL); + + ASSERT_EQ("www.google.com", record_obj->cname()); +} + +TEST(RecordRdataTest, ParsePtrRecord) { + scoped_ptr<PtrRecordRdata> record_obj; + + // These are just the rdata portions of the DNS records, rather than complete + // records, but it works well enough for this test. + + const char record[] = { + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00 + }; + + DnsRecordParser parser(record, sizeof(record), 0); + base::StringPiece record_strpiece(record, sizeof(record)); + + record_obj = PtrRecordRdata::Create(record_strpiece, parser); + ASSERT_TRUE(record_obj != NULL); + + ASSERT_EQ("www.google.com", record_obj->ptrdomain()); +} + +TEST(RecordRdataTest, ParseTxtRecord) { + scoped_ptr<TxtRecordRdata> record_obj; + + // These are just the rdata portions of the DNS records, rather than complete + // records, but it works well enough for this test. + + const char record[] = { + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm' + }; + + DnsRecordParser parser(record, sizeof(record), 0); + base::StringPiece record_strpiece(record, sizeof(record)); + + record_obj = TxtRecordRdata::Create(record_strpiece, parser); + ASSERT_TRUE(record_obj != NULL); + + std::vector<std::string> expected; + expected.push_back("www"); + expected.push_back("google"); + expected.push_back("com"); + + ASSERT_EQ(expected, record_obj->texts()); +} + +} // namespace net diff --git a/net/net.gyp b/net/net.gyp index eb5fc0a..05b9b01 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -444,6 +444,10 @@ 'dns/mapped_host_resolver.h', 'dns/notify_watcher_mac.cc', 'dns/notify_watcher_mac.h', + 'dns/record_parsed.cc', + 'dns/record_parsed.h', + 'dns/record_rdata.cc', + 'dns/record_rdata.h', 'dns/serial_worker.cc', 'dns/serial_worker.h', 'dns/single_request_host_resolver.cc', @@ -1277,6 +1281,14 @@ 'spdy/spdy_websocket_stream.h', ], }], + [ 'enable_mdns != 1', { + 'sources!' : [ + 'dns/record_parsed.cc', + 'dns/record_parsed.h', + 'dns/record_rdata.cc', + 'dns/record_rdata.h', + ] + }], [ 'OS == "win"', { 'sources!': [ 'http/http_auth_handler_ntlm_portable.cc', @@ -1509,6 +1521,8 @@ 'dns/host_resolver_impl_unittest.cc', 'dns/mapped_host_resolver_unittest.cc', 'dns/serial_worker_unittest.cc', + 'dns/record_parsed_unittest.cc', + 'dns/record_rdata_unittest.cc', 'dns/single_request_host_resolver_unittest.cc', 'ftp/ftp_auth_cache_unittest.cc', 'ftp/ftp_ctrl_response_buffer_unittest.cc', @@ -1882,6 +1896,13 @@ ], }, ], + + [ 'enable_mdns != 1', { + 'sources!' : [ + 'dns/record_parsed_unittest.cc', + 'dns/record_rdata_unittest.cc', + ], + }], [ 'OS == "win"', { 'sources!': [ 'dns/dns_config_service_posix_unittest.cc', |