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