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 |