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 |