| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/dns/dns_response.h" | 5 #include "net/dns/dns_response.h" |
| 6 | 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/rand_util.h" |
| 9 #include "net/base/dns_util.h" |
| 10 #include "net/base/net_errors.h" |
| 7 #include "net/base/io_buffer.h" | 11 #include "net/base/io_buffer.h" |
| 8 #include "net/dns/dns_protocol.h" | |
| 9 #include "net/dns/dns_query.h" | 12 #include "net/dns/dns_query.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
| 11 | 14 |
| 12 namespace net { | 15 namespace net { |
| 13 | 16 |
| 14 namespace { | 17 // DNS response consists of a header followed by a question followed by |
| 18 // answer. Header format, question format and response format are |
| 19 // described below. For the meaning of specific fields, please see RFC |
| 20 // 1035. |
| 15 | 21 |
| 16 TEST(DnsRecordParserTest, Constructor) { | 22 // Header format. |
| 17 const char data[] = { 0 }; | 23 // 1 1 1 1 1 1 |
| 24 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| 25 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 26 // | ID | |
| 27 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 28 // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | |
| 29 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 30 // | QDCOUNT | |
| 31 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 32 // | ANCOUNT | |
| 33 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 34 // | NSCOUNT | |
| 35 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 36 // | ARCOUNT | |
| 37 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 18 | 38 |
| 19 EXPECT_FALSE(DnsRecordParser().IsValid()); | 39 // Question format. |
| 20 EXPECT_TRUE(DnsRecordParser(data, 1, 0).IsValid()); | 40 // 1 1 1 1 1 1 |
| 21 EXPECT_TRUE(DnsRecordParser(data, 1, 1).IsValid()); | 41 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| 42 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 43 // | | |
| 44 // / QNAME / |
| 45 // / / |
| 46 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 47 // | QTYPE | |
| 48 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 49 // | QCLASS | |
| 50 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 22 | 51 |
| 23 EXPECT_FALSE(DnsRecordParser(data, 1, 0).AtEnd()); | 52 // Answser format. |
| 24 EXPECT_TRUE(DnsRecordParser(data, 1, 1).AtEnd()); | 53 // 1 1 1 1 1 1 |
| 25 } | 54 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| 55 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 56 // | | |
| 57 // / / |
| 58 // / NAME / |
| 59 // | | |
| 60 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 61 // | TYPE | |
| 62 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 63 // | CLASS | |
| 64 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 65 // | TTL | |
| 66 // | | |
| 67 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 68 // | RDLENGTH | |
| 69 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| |
| 70 // / RDATA / |
| 71 // / / |
| 72 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| 26 | 73 |
| 27 TEST(DnsRecordParserTest, ParseName) { | 74 // TODO(agayev): add more thorough tests. |
| 28 const uint8 data[] = { | 75 TEST(DnsResponseTest, ResponseWithCnameA) { |
| 29 // all labels "foo.example.com" | 76 const std::string kQname("\012codereview\010chromium\003org", 25); |
| 30 0x03, 'f', 'o', 'o', | 77 DnsQuery q1(kQname, kDNS_A, base::Bind(&base::RandInt)); |
| 31 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', | 78 |
| 32 0x03, 'c', 'o', 'm', | 79 uint8 id_hi = q1.id() >> 8, id_lo = q1.id() & 0xff; |
| 33 // byte 0x10 | 80 |
| 34 0x00, | 81 uint8 ip[] = { // codereview.chromium.org resolves to |
| 35 // byte 0x11 | 82 0x4a, 0x7d, 0x5f, 0x79 // 74.125.95.121 |
| 36 // part label, part pointer, "bar.example.com" | |
| 37 0x03, 'b', 'a', 'r', | |
| 38 0xc0, 0x04, | |
| 39 // byte 0x17 | |
| 40 // all pointer to "bar.example.com", 2 jumps | |
| 41 0xc0, 0x11, | |
| 42 // byte 0x1a | |
| 43 }; | 83 }; |
| 44 | 84 |
| 45 std::string out; | 85 IPAddressList expected_ips; |
| 46 DnsRecordParser parser(data, sizeof(data), 0); | 86 expected_ips.push_back(IPAddressNumber(ip, ip + arraysize(ip))); |
| 47 ASSERT_TRUE(parser.IsValid()); | |
| 48 | 87 |
| 49 EXPECT_EQ(0x11, parser.ParseName(data + 0x00, &out)); | 88 uint8 response_data[] = { |
| 50 EXPECT_EQ("foo.example.com", out); | |
| 51 // Check that the last "." is never stored. | |
| 52 out.clear(); | |
| 53 EXPECT_EQ(0x1, parser.ParseName(data + 0x10, &out)); | |
| 54 EXPECT_EQ("", out); | |
| 55 out.clear(); | |
| 56 EXPECT_EQ(0x6, parser.ParseName(data + 0x11, &out)); | |
| 57 EXPECT_EQ("bar.example.com", out); | |
| 58 out.clear(); | |
| 59 EXPECT_EQ(0x2, parser.ParseName(data + 0x17, &out)); | |
| 60 EXPECT_EQ("bar.example.com", out); | |
| 61 | |
| 62 // Parse name without storing it. | |
| 63 EXPECT_EQ(0x11, parser.ParseName(data + 0x00, NULL)); | |
| 64 EXPECT_EQ(0x1, parser.ParseName(data + 0x10, NULL)); | |
| 65 EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL)); | |
| 66 EXPECT_EQ(0x2, parser.ParseName(data + 0x17, NULL)); | |
| 67 | |
| 68 // Check that it works even if initial position is different. | |
| 69 parser = DnsRecordParser(data, sizeof(data), 0x12); | |
| 70 EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL)); | |
| 71 } | |
| 72 | |
| 73 TEST(DnsRecordParserTest, ParseNameFail) { | |
| 74 const uint8 data[] = { | |
| 75 // label length beyond packet | |
| 76 0x30, 'x', 'x', | |
| 77 0x00, | |
| 78 // pointer offset beyond packet | |
| 79 0xc0, 0x20, | |
| 80 // pointer loop | |
| 81 0xc0, 0x08, | |
| 82 0xc0, 0x06, | |
| 83 // incorrect label type (currently supports only direct and pointer) | |
| 84 0x80, 0x00, | |
| 85 // truncated name (missing root label) | |
| 86 0x02, 'x', 'x', | |
| 87 }; | |
| 88 | |
| 89 DnsRecordParser parser(data, sizeof(data), 0); | |
| 90 ASSERT_TRUE(parser.IsValid()); | |
| 91 | |
| 92 std::string out; | |
| 93 EXPECT_EQ(0, parser.ParseName(data + 0x00, &out)); | |
| 94 EXPECT_EQ(0, parser.ParseName(data + 0x04, &out)); | |
| 95 EXPECT_EQ(0, parser.ParseName(data + 0x08, &out)); | |
| 96 EXPECT_EQ(0, parser.ParseName(data + 0x0a, &out)); | |
| 97 EXPECT_EQ(0, parser.ParseName(data + 0x0c, &out)); | |
| 98 EXPECT_EQ(0, parser.ParseName(data + 0x0e, &out)); | |
| 99 } | |
| 100 | |
| 101 TEST(DnsRecordParserTest, ParseRecord) { | |
| 102 const uint8 data[] = { | |
| 103 // Type CNAME record. | |
| 104 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', | |
| 105 0x03, 'c', 'o', 'm', | |
| 106 0x00, | |
| 107 0x00, 0x05, // TYPE is CNAME. | |
| 108 0x00, 0x01, // CLASS is IN. | |
| 109 0x00, 0x01, 0x24, 0x74, // TTL is 0x00012474. | |
| 110 0x00, 0x06, // RDLENGTH is 6 bytes. | |
| 111 0x03, 'f', 'o', 'o', // compressed name in record | |
| 112 0xc0, 0x00, | |
| 113 // Type A record. | |
| 114 0x03, 'b', 'a', 'r', // compressed owner name | |
| 115 0xc0, 0x00, | |
| 116 0x00, 0x01, // TYPE is A. | |
| 117 0x00, 0x01, // CLASS is IN. | |
| 118 0x00, 0x20, 0x13, 0x55, // TTL is 0x00201355. | |
| 119 0x00, 0x04, // RDLENGTH is 4 bytes. | |
| 120 0x7f, 0x02, 0x04, 0x01, // IP is 127.2.4.1 | |
| 121 }; | |
| 122 | |
| 123 std::string out; | |
| 124 DnsRecordParser parser(data, sizeof(data), 0); | |
| 125 | |
| 126 DnsResourceRecord record; | |
| 127 EXPECT_TRUE(parser.ParseRecord(&record)); | |
| 128 EXPECT_EQ("example.com", record.name); | |
| 129 EXPECT_EQ(dns_protocol::kTypeCNAME, record.type); | |
| 130 EXPECT_EQ(dns_protocol::kClassIN, record.klass); | |
| 131 EXPECT_EQ(0x00012474u, record.ttl); | |
| 132 EXPECT_EQ(6u, record.rdata.length()); | |
| 133 EXPECT_EQ(6, parser.ParseName(record.rdata.data(), &out)); | |
| 134 EXPECT_EQ("foo.example.com", out); | |
| 135 EXPECT_FALSE(parser.AtEnd()); | |
| 136 | |
| 137 EXPECT_TRUE(parser.ParseRecord(&record)); | |
| 138 EXPECT_EQ("bar.example.com", record.name); | |
| 139 EXPECT_EQ(dns_protocol::kTypeA, record.type); | |
| 140 EXPECT_EQ(dns_protocol::kClassIN, record.klass); | |
| 141 EXPECT_EQ(0x00201355u, record.ttl); | |
| 142 EXPECT_EQ(4u, record.rdata.length()); | |
| 143 EXPECT_EQ(base::StringPiece("\x7f\x02\x04\x01"), record.rdata); | |
| 144 EXPECT_TRUE(parser.AtEnd()); | |
| 145 | |
| 146 // Test truncated record. | |
| 147 parser = DnsRecordParser(data, sizeof(data) - 2, 0); | |
| 148 EXPECT_TRUE(parser.ParseRecord(&record)); | |
| 149 EXPECT_FALSE(parser.AtEnd()); | |
| 150 EXPECT_FALSE(parser.ParseRecord(&record)); | |
| 151 } | |
| 152 | |
| 153 TEST(DnsResponseTest, InitParse) { | |
| 154 // This includes \0 at the end. | |
| 155 const char qname_data[] = "\x0A""codereview""\x08""chromium""\x03""org"; | |
| 156 const base::StringPiece qname(qname_data, sizeof(qname_data)); | |
| 157 // Compilers want to copy when binding temporary to const &, so must use heap. | |
| 158 scoped_ptr<DnsQuery> query(new DnsQuery(0xcafe, qname, dns_protocol::kTypeA)); | |
| 159 | |
| 160 const uint8 response_data[] = { | |
| 161 // Header | 89 // Header |
| 162 0xca, 0xfe, // ID | 90 id_hi, id_lo, // ID |
| 163 0x81, 0x80, // Standard query response, RA, no error | 91 0x81, 0x80, // Standard query response, no error |
| 164 0x00, 0x01, // 1 question | 92 0x00, 0x01, // 1 question |
| 165 0x00, 0x02, // 2 RRs (answers) | 93 0x00, 0x02, // 2 RRs (answers) |
| 166 0x00, 0x00, // 0 authority RRs | 94 0x00, 0x00, // 0 authority RRs |
| 167 0x00, 0x00, // 0 additional RRs | 95 0x00, 0x00, // 0 additional RRs |
| 168 | 96 |
| 169 // Question | 97 // Question |
| 170 // This part is echoed back from the respective query. | 98 0x0a, 0x63, 0x6f, 0x64, // This part is echoed back from the |
| 171 0x0a, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', | 99 0x65, 0x72, 0x65, 0x76, // respective query. |
| 172 0x08, 'c', 'h', 'r', 'o', 'm', 'i', 'u', 'm', | 100 0x69, 0x65, 0x77, 0x08, |
| 173 0x03, 'o', 'r', 'g', | 101 0x63, 0x68, 0x72, 0x6f, |
| 102 0x6d, 0x69, 0x75, 0x6d, |
| 103 0x03, 0x6f, 0x72, 0x67, |
| 174 0x00, | 104 0x00, |
| 175 0x00, 0x01, // TYPE is A. | 105 0x00, 0x01, |
| 176 0x00, 0x01, // CLASS is IN. | 106 0x00, 0x01, |
| 177 | 107 |
| 178 // Answer 1 | 108 // Answer 1 |
| 179 0xc0, 0x0c, // NAME is a pointer to name in Question section. | 109 0xc0, 0x0c, // NAME is a pointer to name in Question section. |
| 180 0x00, 0x05, // TYPE is CNAME. | 110 0x00, 0x05, // TYPE is CNAME. |
| 181 0x00, 0x01, // CLASS is IN. | 111 0x00, 0x01, // CLASS is IN. |
| 182 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | 112 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. |
| 183 0x24, 0x74, | 113 0x24, 0x74, |
| 184 0x00, 0x12, // RDLENGTH is 18 bytes. | 114 0x00, 0x12, // RDLENGTH is 18 bytse. |
| 185 // ghs.l.google.com in DNS format. | 115 0x03, 0x67, 0x68, 0x73, // ghs.l.google.com in DNS format. |
| 186 0x03, 'g', 'h', 's', | 116 0x01, 0x6c, 0x06, 0x67, |
| 187 0x01, 'l', | 117 0x6f, 0x6f, 0x67, 0x6c, |
| 188 0x06, 'g', 'o', 'o', 'g', 'l', 'e', | 118 0x65, 0x03, 0x63, 0x6f, |
| 189 0x03, 'c', 'o', 'm', | 119 0x6d, 0x00, |
| 190 0x00, | |
| 191 | 120 |
| 192 // Answer 2 | 121 // Answer 2 |
| 193 0xc0, 0x35, // NAME is a pointer to name in Answer 1. | 122 0xc0, 0x35, // NAME is a pointer to name in Question section. |
| 194 0x00, 0x01, // TYPE is A. | 123 0x00, 0x01, // TYPE is A. |
| 195 0x00, 0x01, // CLASS is IN. | 124 0x00, 0x01, // CLASS is IN. |
| 196 0x00, 0x00, // TTL (4 bytes) is 53 seconds. | 125 0x00, 0x00, // TTL (4 bytes) is 53 seconds. |
| 197 0x00, 0x35, | 126 0x00, 0x35, |
| 198 0x00, 0x04, // RDLENGTH is 4 bytes. | 127 0x00, 0x04, // RDLENGTH is 4 bytes. |
| 199 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121 | 128 ip[0], ip[1], ip[2], ip[3], // RDATA is the IP. |
| 200 0x5f, 0x79, | |
| 201 }; | 129 }; |
| 202 | 130 |
| 203 DnsResponse resp; | 131 // Create a response object and simulate reading into it. |
| 204 memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data)); | 132 DnsResponse r1(&q1); |
| 133 memcpy(r1.io_buffer()->data(), &response_data[0], |
| 134 r1.io_buffer()->size()); |
| 205 | 135 |
| 206 // Reject too short. | 136 // Verify resolved IPs. |
| 207 EXPECT_FALSE(resp.InitParse(query->io_buffer()->size() - 1, *query)); | 137 int response_size = arraysize(response_data); |
| 208 | 138 IPAddressList actual_ips; |
| 209 // Reject wrong id. | 139 EXPECT_EQ(OK, r1.Parse(response_size, &actual_ips)); |
| 210 scoped_ptr<DnsQuery> other_query(query->CloneWithNewId(0xbeef)); | 140 EXPECT_EQ(expected_ips, actual_ips); |
| 211 EXPECT_FALSE(resp.InitParse(sizeof(response_data), *other_query)); | |
| 212 | |
| 213 // Reject wrong question. | |
| 214 scoped_ptr<DnsQuery> wrong_query( | |
| 215 new DnsQuery(0xcafe, qname, dns_protocol::kTypeCNAME)); | |
| 216 EXPECT_FALSE(resp.InitParse(sizeof(response_data), *wrong_query)); | |
| 217 | |
| 218 // Accept matching question. | |
| 219 EXPECT_TRUE(resp.InitParse(sizeof(response_data), *query)); | |
| 220 | |
| 221 // Check header access. | |
| 222 EXPECT_EQ(0x81, resp.flags0()); | |
| 223 EXPECT_EQ(0x80, resp.flags1()); | |
| 224 EXPECT_EQ(0x0, resp.rcode()); | |
| 225 EXPECT_EQ(2, resp.answer_count()); | |
| 226 | |
| 227 DnsResourceRecord record; | |
| 228 DnsRecordParser parser = resp.Parser(); | |
| 229 EXPECT_TRUE(parser.ParseRecord(&record)); | |
| 230 EXPECT_TRUE(parser.ParseRecord(&record)); | |
| 231 EXPECT_FALSE(parser.ParseRecord(&record)); | |
| 232 } | 141 } |
| 233 | 142 |
| 234 } // namespace | |
| 235 | |
| 236 } // namespace net | 143 } // namespace net |
| OLD | NEW |