| Index: net/dns/dns_response_unittest.cc
|
| diff --git a/net/dns/dns_response_unittest.cc b/net/dns/dns_response_unittest.cc
|
| index 775cfc6829aea8dde8698edbb320799eb5cefdc1..94c630f6eed12b386a5493ed05ed83a092d1afb7 100644
|
| --- a/net/dns/dns_response_unittest.cc
|
| +++ b/net/dns/dns_response_unittest.cc
|
| @@ -4,106 +4,175 @@
|
|
|
| #include "net/dns/dns_response.h"
|
|
|
| -#include "base/bind.h"
|
| -#include "base/rand_util.h"
|
| -#include "net/base/dns_util.h"
|
| -#include "net/base/net_errors.h"
|
| #include "net/base/io_buffer.h"
|
| #include "net/dns/dns_query.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace net {
|
|
|
| -// DNS response consists of a header followed by a question followed by
|
| -// answer. Header format, question format and response format are
|
| -// described below. For the meaning of specific fields, please see RFC
|
| -// 1035.
|
| -
|
| -// Header format.
|
| -// 1 1 1 1 1 1
|
| -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | ID |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | QDCOUNT |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | ANCOUNT |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | NSCOUNT |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | ARCOUNT |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -
|
| -// Question format.
|
| -// 1 1 1 1 1 1
|
| -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | |
|
| -// / QNAME /
|
| -// / /
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | QTYPE |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | QCLASS |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -
|
| -// Answser format.
|
| -// 1 1 1 1 1 1
|
| -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | |
|
| -// / /
|
| -// / NAME /
|
| -// | |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | TYPE |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | CLASS |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | TTL |
|
| -// | |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -// | RDLENGTH |
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
|
| -// / RDATA /
|
| -// / /
|
| -// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
| -
|
| -// TODO(agayev): add more thorough tests.
|
| -TEST(DnsResponseTest, ResponseWithCnameA) {
|
| - const std::string kQname("\012codereview\010chromium\003org", 25);
|
| - DnsQuery q1(kQname, kDNS_A, base::Bind(&base::RandInt));
|
| -
|
| - uint8 id_hi = q1.id() >> 8, id_lo = q1.id() & 0xff;
|
| -
|
| - uint8 ip[] = { // codereview.chromium.org resolves to
|
| - 0x4a, 0x7d, 0x5f, 0x79 // 74.125.95.121
|
| +namespace {
|
| +
|
| +TEST(DnsRecordParserTest, Constructor) {
|
| + const char data[] = { 0 };
|
| +
|
| + EXPECT_FALSE(DnsRecordParser().IsValid());
|
| + EXPECT_TRUE(DnsRecordParser(data, 1, 0).IsValid());
|
| + EXPECT_TRUE(DnsRecordParser(data, 1, 1).IsValid());
|
| +
|
| + EXPECT_FALSE(DnsRecordParser(data, 1, 0).AtEnd());
|
| + EXPECT_TRUE(DnsRecordParser(data, 1, 1).AtEnd());
|
| +}
|
| +
|
| +TEST(DnsRecordParserTest, ParseName) {
|
| + const uint8 data[] = {
|
| + // all labels "foo.example.com"
|
| + 0x03, 'f', 'o', 'o',
|
| + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
| + 0x03, 'c', 'o', 'm',
|
| + // byte 0x10
|
| + 0x00,
|
| + // byte 0x11
|
| + // part label, part pointer, "bar.example.com"
|
| + 0x03, 'b', 'a', 'r',
|
| + 0xc0, 0x04,
|
| + // byte 0x17
|
| + // all pointer to "bar.example.com", 2 jumps
|
| + 0xc0, 0x11,
|
| + // byte 0x1a
|
| + };
|
| +
|
| + std::string out;
|
| + DnsRecordParser parser(data, sizeof(data), 0);
|
| + ASSERT_TRUE(parser.IsValid());
|
| +
|
| + EXPECT_EQ(0x11, parser.ParseName(data + 0x00, &out));
|
| + EXPECT_EQ("foo.example.com", out);
|
| + // Check that the last "." is never stored.
|
| + out.clear();
|
| + EXPECT_EQ(0x1, parser.ParseName(data + 0x10, &out));
|
| + EXPECT_EQ("", out);
|
| + out.clear();
|
| + EXPECT_EQ(0x6, parser.ParseName(data + 0x11, &out));
|
| + EXPECT_EQ("bar.example.com", out);
|
| + out.clear();
|
| + EXPECT_EQ(0x2, parser.ParseName(data + 0x17, &out));
|
| + EXPECT_EQ("bar.example.com", out);
|
| +
|
| + // Parse name without storing it.
|
| + EXPECT_EQ(0x11, parser.ParseName(data + 0x00, NULL));
|
| + EXPECT_EQ(0x1, parser.ParseName(data + 0x10, NULL));
|
| + EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL));
|
| + EXPECT_EQ(0x2, parser.ParseName(data + 0x17, NULL));
|
| +
|
| + // Check that it works even if initial position is different.
|
| + parser = DnsRecordParser(data, sizeof(data), 0x12);
|
| + EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL));
|
| +}
|
| +
|
| +TEST(DnsRecordParserTest, ParseNameFail) {
|
| + const uint8 data[] = {
|
| + // label length beyond packet
|
| + 0x30, 'x', 'x',
|
| + 0x00,
|
| + // pointer offset beyond packet
|
| + 0xc0, 0x20,
|
| + // pointer loop
|
| + 0xc0, 0x08,
|
| + 0xc0, 0x06,
|
| + // incorrect label type (currently supports only direct and pointer)
|
| + 0x80, 0x00,
|
| + // truncated name (missing root label)
|
| + 0x02, 'x', 'x',
|
| + };
|
| +
|
| + DnsRecordParser parser(data, sizeof(data), 0);
|
| + ASSERT_TRUE(parser.IsValid());
|
| +
|
| + std::string out;
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x00, &out));
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x04, &out));
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x08, &out));
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x0a, &out));
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x0c, &out));
|
| + EXPECT_EQ(0, parser.ParseName(data + 0x0e, &out));
|
| +}
|
| +
|
| +TEST(DnsRecordParserTest, ParseRecord) {
|
| + const uint8 data[] = {
|
| + // Type CNAME record.
|
| + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
| + 0x03, 'c', 'o', 'm',
|
| + 0x00,
|
| + 0x00, 0x05, // TYPE is CNAME.
|
| + 0x00, 0x01, // CLASS is IN.
|
| + 0x00, 0x01, 0x24, 0x74, // TTL is 0x00012474.
|
| + 0x00, 0x06, // RDLENGTH is 6 bytes.
|
| + 0x03, 'f', 'o', 'o', // compressed name in record
|
| + 0xc0, 0x00,
|
| + // Type A record.
|
| + 0x03, 'b', 'a', 'r', // compressed owner name
|
| + 0xc0, 0x00,
|
| + 0x00, 0x01, // TYPE is A.
|
| + 0x00, 0x01, // CLASS is IN.
|
| + 0x00, 0x20, 0x13, 0x55, // TTL is 0x00201355.
|
| + 0x00, 0x04, // RDLENGTH is 4 bytes.
|
| + 0x7f, 0x02, 0x04, 0x01, // IP is 127.2.4.1
|
| };
|
|
|
| - IPAddressList expected_ips;
|
| - expected_ips.push_back(IPAddressNumber(ip, ip + arraysize(ip)));
|
| + std::string out;
|
| + DnsRecordParser parser(data, sizeof(data), 0);
|
| +
|
| + DnsResourceRecord record;
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + EXPECT_EQ("example.com", record.name);
|
| + EXPECT_EQ(dns_protocol::kTypeCNAME, record.type);
|
| + EXPECT_EQ(dns_protocol::kClassIN, record.klass);
|
| + EXPECT_EQ(0x00012474u, record.ttl);
|
| + EXPECT_EQ(6u, record.rdata.length());
|
| + EXPECT_EQ(6, parser.ParseName(record.rdata.data(), &out));
|
| + EXPECT_EQ("foo.example.com", out);
|
| + EXPECT_FALSE(parser.AtEnd());
|
| +
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + EXPECT_EQ("bar.example.com", record.name);
|
| + EXPECT_EQ(dns_protocol::kTypeA, record.type);
|
| + EXPECT_EQ(dns_protocol::kClassIN, record.klass);
|
| + EXPECT_EQ(0x00201355u, record.ttl);
|
| + EXPECT_EQ(4u, record.rdata.length());
|
| + EXPECT_EQ(base::StringPiece("\x7f\x02\x04\x01"), record.rdata);
|
| + EXPECT_TRUE(parser.AtEnd());
|
| +
|
| + // Test truncated record.
|
| + parser = DnsRecordParser(data, sizeof(data) - 2, 0);
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + EXPECT_FALSE(parser.AtEnd());
|
| + EXPECT_FALSE(parser.ParseRecord(&record));
|
| +}
|
| +
|
| +TEST(DnsResponseTest, InitParse) {
|
| + // This includes \0 at the end.
|
| + const char qname_data[] = "\x0A""codereview""\x08""chromium""\x03""org";
|
| + const base::StringPiece qname(qname_data, sizeof(qname_data));
|
| + // Compilers want to copy when binding temporary to const &, so must use heap.
|
| + scoped_ptr<DnsQuery> query(new DnsQuery(0xcafe, qname, dns_protocol::kTypeA));
|
|
|
| - uint8 response_data[] = {
|
| + const uint8 response_data[] = {
|
| // Header
|
| - id_hi, id_lo, // ID
|
| - 0x81, 0x80, // Standard query response, no error
|
| + 0xca, 0xfe, // ID
|
| + 0x81, 0x80, // Standard query response, RA, no error
|
| 0x00, 0x01, // 1 question
|
| 0x00, 0x02, // 2 RRs (answers)
|
| 0x00, 0x00, // 0 authority RRs
|
| 0x00, 0x00, // 0 additional RRs
|
|
|
| // Question
|
| - 0x0a, 0x63, 0x6f, 0x64, // This part is echoed back from the
|
| - 0x65, 0x72, 0x65, 0x76, // respective query.
|
| - 0x69, 0x65, 0x77, 0x08,
|
| - 0x63, 0x68, 0x72, 0x6f,
|
| - 0x6d, 0x69, 0x75, 0x6d,
|
| - 0x03, 0x6f, 0x72, 0x67,
|
| + // This part is echoed back from the respective query.
|
| + 0x0a, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w',
|
| + 0x08, 'c', 'h', 'r', 'o', 'm', 'i', 'u', 'm',
|
| + 0x03, 'o', 'r', 'g',
|
| 0x00,
|
| - 0x00, 0x01,
|
| - 0x00, 0x01,
|
| + 0x00, 0x01, // TYPE is A.
|
| + 0x00, 0x01, // CLASS is IN.
|
|
|
| // Answer 1
|
| 0xc0, 0x0c, // NAME is a pointer to name in Question section.
|
| @@ -111,33 +180,56 @@ TEST(DnsResponseTest, ResponseWithCnameA) {
|
| 0x00, 0x01, // CLASS is IN.
|
| 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
|
| 0x24, 0x74,
|
| - 0x00, 0x12, // RDLENGTH is 18 bytse.
|
| - 0x03, 0x67, 0x68, 0x73, // ghs.l.google.com in DNS format.
|
| - 0x01, 0x6c, 0x06, 0x67,
|
| - 0x6f, 0x6f, 0x67, 0x6c,
|
| - 0x65, 0x03, 0x63, 0x6f,
|
| - 0x6d, 0x00,
|
| + 0x00, 0x12, // RDLENGTH is 18 bytes.
|
| + // ghs.l.google.com in DNS format.
|
| + 0x03, 'g', 'h', 's',
|
| + 0x01, 'l',
|
| + 0x06, 'g', 'o', 'o', 'g', 'l', 'e',
|
| + 0x03, 'c', 'o', 'm',
|
| + 0x00,
|
|
|
| // Answer 2
|
| - 0xc0, 0x35, // NAME is a pointer to name in Question section.
|
| + 0xc0, 0x35, // NAME is a pointer to name in Answer 1.
|
| 0x00, 0x01, // TYPE is A.
|
| 0x00, 0x01, // CLASS is IN.
|
| 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
|
| 0x00, 0x35,
|
| - 0x00, 0x04, // RDLENGTH is 4 bytes.
|
| - ip[0], ip[1], ip[2], ip[3], // RDATA is the IP.
|
| + 0x00, 0x04, // RDLENGTH is 4 bytes.
|
| + 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
|
| + 0x5f, 0x79,
|
| };
|
|
|
| - // Create a response object and simulate reading into it.
|
| - DnsResponse r1(&q1);
|
| - memcpy(r1.io_buffer()->data(), &response_data[0],
|
| - r1.io_buffer()->size());
|
| + DnsResponse resp;
|
| + memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data));
|
|
|
| - // Verify resolved IPs.
|
| - int response_size = arraysize(response_data);
|
| - IPAddressList actual_ips;
|
| - EXPECT_EQ(OK, r1.Parse(response_size, &actual_ips));
|
| - EXPECT_EQ(expected_ips, actual_ips);
|
| + // Reject too short.
|
| + EXPECT_FALSE(resp.InitParse(query->io_buffer()->size() - 1, *query));
|
| +
|
| + // Reject wrong id.
|
| + scoped_ptr<DnsQuery> other_query(query->CloneWithNewId(0xbeef));
|
| + EXPECT_FALSE(resp.InitParse(sizeof(response_data), *other_query));
|
| +
|
| + // Reject wrong question.
|
| + scoped_ptr<DnsQuery> wrong_query(
|
| + new DnsQuery(0xcafe, qname, dns_protocol::kTypeCNAME));
|
| + EXPECT_FALSE(resp.InitParse(sizeof(response_data), *wrong_query));
|
| +
|
| + // Accept matching question.
|
| + EXPECT_TRUE(resp.InitParse(sizeof(response_data), *query));
|
| +
|
| + // Check header access.
|
| + EXPECT_EQ(0x81, resp.flags0());
|
| + EXPECT_EQ(0x80, resp.flags1());
|
| + EXPECT_EQ(0x0, resp.rcode());
|
| + EXPECT_EQ(2, resp.answer_count());
|
| +
|
| + DnsResourceRecord record;
|
| + DnsRecordParser parser = resp.Parser();
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + EXPECT_FALSE(parser.ParseRecord(&record));
|
| }
|
|
|
| +} // namespace
|
| +
|
| } // namespace net
|
|
|