blob: e221aaf3687b754c416195341d0b64d5dcb7ed10 [file] [log] [blame]
Jason Jeremy Imana21be272020-10-21 17:53:45 +09001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "patchpanel/dns/dns_response.h"
6
7#include <algorithm>
8#include <memory>
9
10#include "base/big_endian.h"
11#include "base/optional.h"
12#include "base/stl_util.h"
13#include "base/time/time.h"
14
15#include "patchpanel/dns/dns_protocol.h"
16#include "patchpanel/dns/dns_query.h"
17#include "patchpanel/dns/dns_util.h"
18#include "patchpanel/dns/io_buffer.h"
19
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
Qijiang Fan713061e2021-03-08 15:45:12 +090023#include <base/check.h>
24
Jason Jeremy Imana21be272020-10-21 17:53:45 +090025namespace patchpanel {
26
27namespace {
28
29// Taken from Chromium's "net/dns/dns_test_util.h".
30// Query/response set for www.google.com, ID is fixed to 0.
31static const char kT0HostName[] = "www.google.com";
32static const uint16_t kT0Qtype = dns_protocol::kTypeA;
33static const uint8_t kT0ResponseDatagram[] = {
34 // response contains one CNAME for www.l.google.com and the following
35 // IP addresses: 74.125.226.{179,180,176,177,178}
36 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
37 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
38 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
39 0x00, 0x01, 0x00, 0x01, 0x4d, 0x13, 0x00, 0x08, 0x03, 0x77, 0x77, 0x77,
40 0x01, 0x6c, 0xc0, 0x10, 0xc0, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
41 0x00, 0xe4, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb3, 0xc0, 0x2c, 0x00, 0x01,
42 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb4,
43 0xc0, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04,
44 0x4a, 0x7d, 0xe2, 0xb0, 0xc0, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
45 0x00, 0xe4, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb1, 0xc0, 0x2c, 0x00, 0x01,
46 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb2};
47static const char* const kT0IpAddresses[] = {
48 "74.125.226.179", "74.125.226.180", "74.125.226.176",
49 "74.125.226.177", "74.125.226.178"
50};
51// +1 for the CNAME record.
52static const unsigned kT0RecordCount = base::size(kT0IpAddresses) + 1;
53
54TEST(DnsRecordParserTest, Constructor) {
55 const char data[] = { 0 };
56
57 EXPECT_FALSE(DnsRecordParser().IsValid());
58 EXPECT_TRUE(DnsRecordParser(data, 1, 0).IsValid());
59 EXPECT_TRUE(DnsRecordParser(data, 1, 1).IsValid());
60
61 EXPECT_FALSE(DnsRecordParser(data, 1, 0).AtEnd());
62 EXPECT_TRUE(DnsRecordParser(data, 1, 1).AtEnd());
63}
64
65TEST(DnsRecordParserTest, ReadName) {
66 const uint8_t data[] = {
67 // all labels "foo.example.com"
68 0x03, 'f', 'o', 'o', 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0x03, 'c',
69 'o', 'm',
70 // byte 0x10
71 0x00,
72 // byte 0x11
73 // part label, part pointer, "bar.example.com"
74 0x03, 'b', 'a', 'r', 0xc0, 0x04,
75 // byte 0x17
76 // all pointer to "bar.example.com", 2 jumps
77 0xc0, 0x11,
78 // byte 0x1a
79 };
80
81 std::string out;
82 DnsRecordParser parser(data, sizeof(data), 0);
83 ASSERT_TRUE(parser.IsValid());
84
85 EXPECT_EQ(0x11u, parser.ReadName(data + 0x00, &out));
86 EXPECT_EQ("foo.example.com", out);
87 // Check that the last "." is never stored.
88 out.clear();
89 EXPECT_EQ(0x1u, parser.ReadName(data + 0x10, &out));
90 EXPECT_EQ("", out);
91 out.clear();
92 EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, &out));
93 EXPECT_EQ("bar.example.com", out);
94 out.clear();
95 EXPECT_EQ(0x2u, parser.ReadName(data + 0x17, &out));
96 EXPECT_EQ("bar.example.com", out);
97
98 // Parse name without storing it.
99 EXPECT_EQ(0x11u, parser.ReadName(data + 0x00, nullptr));
100 EXPECT_EQ(0x1u, parser.ReadName(data + 0x10, nullptr));
101 EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, nullptr));
102 EXPECT_EQ(0x2u, parser.ReadName(data + 0x17, nullptr));
103
104 // Check that it works even if initial position is different.
105 parser = DnsRecordParser(data, sizeof(data), 0x12);
106 EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, nullptr));
107}
108
109TEST(DnsRecordParserTest, ReadNameFail) {
110 const uint8_t data[] = {
111 // label length beyond packet
112 0x30, 'x', 'x', 0x00,
113 // pointer offset beyond packet
114 0xc0, 0x20,
115 // pointer loop
116 0xc0, 0x08, 0xc0, 0x06,
117 // incorrect label type (currently supports only direct and pointer)
118 0x80, 0x00,
119 // truncated name (missing root label)
120 0x02, 'x', 'x',
121 };
122
123 DnsRecordParser parser(data, sizeof(data), 0);
124 ASSERT_TRUE(parser.IsValid());
125
126 std::string out;
127 EXPECT_EQ(0u, parser.ReadName(data + 0x00, &out));
128 EXPECT_EQ(0u, parser.ReadName(data + 0x04, &out));
129 EXPECT_EQ(0u, parser.ReadName(data + 0x08, &out));
130 EXPECT_EQ(0u, parser.ReadName(data + 0x0a, &out));
131 EXPECT_EQ(0u, parser.ReadName(data + 0x0c, &out));
132 EXPECT_EQ(0u, parser.ReadName(data + 0x0e, &out));
133}
134
135// Returns an RFC 1034 style domain name with a length of |name_len|.
136// Also writes the expected dotted string representation into |dotted_str|,
137// which must be non-null.
138std::vector<uint8_t> BuildRfc1034Name(const size_t name_len,
139 std::string* dotted_str) {
140 CHECK(dotted_str != nullptr);
141 auto ChoosePrintableCharLambda = [](uint8_t n) { return n % 26 + 'A'; };
142 const size_t max_label_len = 63;
143 std::vector<uint8_t> data;
144
145 dotted_str->clear();
146 while (data.size() < name_len) {
147 // Write the null label representing the root node.
148 if (data.size() == name_len - 1) {
149 data.push_back(0);
150 break;
151 }
152
153 // Compute the size of the next label.
154 //
155 // Suppose |name_len| is 8 and |data.size()| is 4. We want |label_len| to be
156 // 2 so that we are correctly aligned to put 0 in the final position.
157 //
158 // 3 'A' 'B' 'C' _ _ _ _
159 // 0 1 2 3 4 5 6 7
160 const size_t label_len =
161 std::min(name_len - data.size() - 2, max_label_len);
162 // Write the length octet
163 data.push_back(label_len);
164
165 // Write |label_len| bytes of label data
166 const size_t size_with_label = data.size() + label_len;
167 while (data.size() < size_with_label) {
168 const uint8_t chr = ChoosePrintableCharLambda(data.size());
169 data.push_back(chr);
170 dotted_str->push_back(chr);
171
172 CHECK(data.size() <= name_len);
173 }
174
175 // Write a trailing dot after every label
176 dotted_str->push_back('.');
177 }
178
179 // Omit the final dot
180 if (!dotted_str->empty())
181 dotted_str->pop_back();
182
183 CHECK(data.size() == name_len);
184 return data;
185}
186
187TEST(DnsRecordParserTest, ReadNameGoodLength) {
188 const size_t name_len_cases[] = {1, 10, 40, 250, 254, 255};
189
190 for (auto name_len : name_len_cases) {
191 std::string expected_name;
192 const std::vector<uint8_t> data_vector =
193 BuildRfc1034Name(name_len, &expected_name);
194 const uint8_t* data = data_vector.data();
195
196 DnsRecordParser parser(data, name_len, 0);
197 ASSERT_TRUE(parser.IsValid());
198
199 std::string out;
200 EXPECT_EQ(name_len, parser.ReadName(data, &out));
201 EXPECT_EQ(expected_name, out);
202 }
203}
204
205TEST(DnsRecordParserTest, ReadNameTooLongFail) {
206 const size_t name_len_cases[] = {256, 257, 258, 300, 10000};
207
208 for (auto name_len : name_len_cases) {
209 std::string expected_name;
210 const std::vector<uint8_t> data_vector =
211 BuildRfc1034Name(name_len, &expected_name);
212 const uint8_t* data = data_vector.data();
213
214 DnsRecordParser parser(data, name_len, 0);
215 ASSERT_TRUE(parser.IsValid());
216
217 std::string out;
218 EXPECT_EQ(0u, parser.ReadName(data, &out));
219 }
220}
221
222TEST(DnsRecordParserTest, ReadRecord) {
223 const uint8_t data[] = {
224 // Type CNAME record.
225 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm', 0x00, 0x00,
226 0x05, // TYPE is CNAME.
227 0x00, 0x01, // CLASS is IN.
228 0x00, 0x01, 0x24, 0x74, // TTL is 0x00012474.
229 0x00, 0x06, // RDLENGTH is 6 bytes.
230 0x03, 'f', 'o', 'o', // compressed name in record
231 0xc0, 0x00,
232 // Type A record.
233 0x03, 'b', 'a', 'r', // compressed owner name
234 0xc0, 0x00, 0x00, 0x01, // TYPE is A.
235 0x00, 0x01, // CLASS is IN.
236 0x00, 0x20, 0x13, 0x55, // TTL is 0x00201355.
237 0x00, 0x04, // RDLENGTH is 4 bytes.
238 0x7f, 0x02, 0x04, 0x01, // IP is 127.2.4.1
239 };
240
241 std::string out;
242 DnsRecordParser parser(data, sizeof(data), 0);
243
244 DnsResourceRecord record;
245 EXPECT_TRUE(parser.ReadRecord(&record));
246 EXPECT_EQ("example.com", record.name);
247 EXPECT_EQ(dns_protocol::kTypeCNAME, record.type);
248 EXPECT_EQ(dns_protocol::kClassIN, record.klass);
249 EXPECT_EQ(0x00012474u, record.ttl);
250 EXPECT_EQ(6u, record.rdata.length());
251 EXPECT_EQ(6u, parser.ReadName(record.rdata.data(), &out));
252 EXPECT_EQ("foo.example.com", out);
253 EXPECT_FALSE(parser.AtEnd());
254
255 EXPECT_TRUE(parser.ReadRecord(&record));
256 EXPECT_EQ("bar.example.com", record.name);
257 EXPECT_EQ(dns_protocol::kTypeA, record.type);
258 EXPECT_EQ(dns_protocol::kClassIN, record.klass);
259 EXPECT_EQ(0x00201355u, record.ttl);
260 EXPECT_EQ(4u, record.rdata.length());
261 EXPECT_EQ(base::StringPiece("\x7f\x02\x04\x01"), record.rdata);
262 EXPECT_TRUE(parser.AtEnd());
263
264 // Test truncated record.
265 parser = DnsRecordParser(data, sizeof(data) - 2, 0);
266 EXPECT_TRUE(parser.ReadRecord(&record));
267 EXPECT_FALSE(parser.AtEnd());
268 EXPECT_FALSE(parser.ReadRecord(&record));
269}
270
271TEST(DnsResponseTest, InitParseWithoutQuery) {
272 DnsResponse resp;
273 memcpy(resp.io_buffer()->data(), kT0ResponseDatagram,
274 sizeof(kT0ResponseDatagram));
275
276 // Accept matching question.
277 EXPECT_TRUE(resp.InitParseWithoutQuery(sizeof(kT0ResponseDatagram)));
278 EXPECT_TRUE(resp.IsValid());
279
280 // Check header access.
281 EXPECT_EQ(0x8180, resp.flags());
282 EXPECT_EQ(0x0, resp.rcode());
283 EXPECT_EQ(kT0RecordCount, resp.answer_count());
284
285 // Check question access.
286 EXPECT_EQ(kT0Qtype, resp.qtype());
287 EXPECT_EQ(kT0HostName, resp.GetDottedName());
288
289 DnsResourceRecord record;
290 DnsRecordParser parser = resp.Parser();
291 for (unsigned i = 0; i < kT0RecordCount; i ++) {
292 EXPECT_FALSE(parser.AtEnd());
293 EXPECT_TRUE(parser.ReadRecord(&record));
294 }
295 EXPECT_TRUE(parser.AtEnd());
296 EXPECT_FALSE(parser.ReadRecord(&record));
297}
298
299TEST(DnsResponseTest, InitParseWithoutQueryNoQuestions) {
300 const uint8_t response_data[] = {
301 // Header
302 0xca, 0xfe, // ID
303 0x81, 0x80, // Standard query response, RA, no error
304 0x00, 0x00, // No question
305 0x00, 0x01, // 2 RRs (answers)
306 0x00, 0x00, // 0 authority RRs
307 0x00, 0x00, // 0 additional RRs
308
309 // Answer 1
310 0x0a, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', 0x08, 'c', 'h',
311 'r', 'o', 'm', 'i', 'u', 'm', 0x03, 'o', 'r', 'g', 0x00, 0x00,
312 0x01, // TYPE is A.
313 0x00, 0x01, // CLASS is IN.
314 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
315 0x00, 0x35, 0x00, 0x04, // RDLENGTH is 4 bytes.
316 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
317 0x5f, 0x79,
318 };
319
320 DnsResponse resp;
321 memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data));
322
323 EXPECT_TRUE(resp.InitParseWithoutQuery(sizeof(response_data)));
324
325 // Check header access.
326 EXPECT_THAT(resp.id(), testing::Optional(0xcafe));
327 EXPECT_EQ(0x8180, resp.flags());
328 EXPECT_EQ(0x0, resp.rcode());
329 EXPECT_EQ(0x1u, resp.answer_count());
330
331 DnsResourceRecord record;
332 DnsRecordParser parser = resp.Parser();
333
334 EXPECT_FALSE(parser.AtEnd());
335 EXPECT_TRUE(parser.ReadRecord(&record));
336 EXPECT_EQ("codereview.chromium.org", record.name);
337 EXPECT_EQ(0x00000035u, record.ttl);
338 EXPECT_EQ(dns_protocol::kTypeA, record.type);
339
340 EXPECT_TRUE(parser.AtEnd());
341 EXPECT_FALSE(parser.ReadRecord(&record));
342}
343
344TEST(DnsResponseTest, InitParseWithoutQueryInvalidFlags) {
345 const uint8_t response_data[] = {
346 // Header
347 0xca, 0xfe, // ID
348 0x01, 0x80, // RA, no error. Note the absence of the required QR bit.
349 0x00, 0x00, // No question
350 0x00, 0x01, // 2 RRs (answers)
351 0x00, 0x00, // 0 authority RRs
352 0x00, 0x00, // 0 additional RRs
353
354 // Answer 1
355 0x0a, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', 0x08, 'c', 'h',
356 'r', 'o', 'm', 'i', 'u', 'm', 0x03, 'o', 'r', 'g', 0x00, 0x00,
357 0x01, // TYPE is A.
358 0x00, 0x01, // CLASS is IN.
359 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
360 0x00, 0x35, 0x00, 0x04, // RDLENGTH is 4 bytes.
361 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
362 0x5f, 0x79,
363 };
364
365 DnsResponse resp;
366 memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data));
367
368 EXPECT_FALSE(resp.InitParseWithoutQuery(sizeof(response_data)));
369 EXPECT_THAT(resp.id(), testing::Optional(0xcafe));
370}
371
372TEST(DnsResponseTest, InitParseWithoutQueryTwoQuestions) {
373 const uint8_t response_data[] = {
374 // Header
375 0xca, 0xfe, // ID
376 0x81, 0x80, // Standard query response, RA, no error
377 0x00, 0x02, // 2 questions
378 0x00, 0x01, // 2 RRs (answers)
379 0x00, 0x00, // 0 authority RRs
380 0x00, 0x00, // 0 additional RRs
381
382 // Question 1
383 0x0a, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', 0x08, 'c', 'h',
384 'r', 'o', 'm', 'i', 'u', 'm', 0x03, 'o', 'r', 'g', 0x00, 0x00,
385 0x01, // TYPE is A.
386 0x00, 0x01, // CLASS is IN.
387
388 // Question 2
389 0x0b, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', '2', 0xc0,
390 0x18, // pointer to "chromium.org"
391 0x00, 0x01, // TYPE is A.
392 0x00, 0x01, // CLASS is IN.
393
394 // Answer 1
395 0xc0, 0x0c, // NAME is a pointer to name in Question section.
396 0x00, 0x01, // TYPE is A.
397 0x00, 0x01, // CLASS is IN.
398 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
399 0x00, 0x35, 0x00, 0x04, // RDLENGTH is 4 bytes.
400 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
401 0x5f, 0x79,
402 };
403
404 DnsResponse resp;
405 memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data));
406
407 EXPECT_TRUE(resp.InitParseWithoutQuery(sizeof(response_data)));
408
409 // Check header access.
410 EXPECT_EQ(0x8180, resp.flags());
411 EXPECT_EQ(0x0, resp.rcode());
412 EXPECT_EQ(0x01u, resp.answer_count());
413
414 DnsResourceRecord record;
415 DnsRecordParser parser = resp.Parser();
416
417 EXPECT_FALSE(parser.AtEnd());
418 EXPECT_TRUE(parser.ReadRecord(&record));
419 EXPECT_EQ("codereview.chromium.org", record.name);
420 EXPECT_EQ(0x35u, record.ttl);
421 EXPECT_EQ(dns_protocol::kTypeA, record.type);
422
423 EXPECT_TRUE(parser.AtEnd());
424 EXPECT_FALSE(parser.ReadRecord(&record));
425}
426
427TEST(DnsResponseTest, InitParseWithoutQueryPacketTooShort) {
428 const uint8_t response_data[] = {
429 // Header
430 0xca, 0xfe, // ID
431 0x81, 0x80, // Standard query response, RA, no error
432 0x00, 0x00, // No question
433 };
434
435 DnsResponse resp;
436 memcpy(resp.io_buffer()->data(), response_data, sizeof(response_data));
437
438 EXPECT_FALSE(resp.InitParseWithoutQuery(sizeof(response_data)));
439}
440
441TEST(DnsResponseWriteTest, SingleARecordAnswer) {
442 const uint8_t response_data[] = {
443 0x12, 0x34, // ID
444 0x84, 0x00, // flags, response with authoritative answer
445 0x00, 0x00, // number of questions
446 0x00, 0x01, // number of answer rr
447 0x00, 0x00, // number of name server rr
448 0x00, 0x00, // number of additional rr
449 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
450 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
451 0x00, // null label
452 0x00, 0x01, // type A Record
453 0x00, 0x01, // class IN
454 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
455 0x00, 0x04, // rdlength, 32 bits
456 0xc0, 0xa8, 0x00, 0x01, // 192.168.0.1
457 };
458 patchpanel::DnsResourceRecord answer;
459 answer.name = "www.example.com";
460 answer.type = dns_protocol::kTypeA;
461 answer.klass = dns_protocol::kClassIN;
462 answer.ttl = 120; // 120 seconds.
463 answer.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
464 std::vector<DnsResourceRecord> answers(1, answer);
465 DnsResponse response(0x1234 /* response_id */, true /* is_authoritative*/,
466 answers, {} /* authority_records */,
467 {} /* additional records */, base::nullopt);
468 ASSERT_NE(nullptr, response.io_buffer());
469 EXPECT_TRUE(response.IsValid());
470 std::string expected_response(reinterpret_cast<const char*>(response_data),
471 sizeof(response_data));
472 std::string actual_response(response.io_buffer()->data(),
473 response.io_buffer_size());
474 EXPECT_EQ(expected_response, actual_response);
475}
476
477TEST(DnsResponseWriteTest, SingleARecordAnswerWithFinalDotInName) {
478 const uint8_t response_data[] = {
479 0x12, 0x34, // ID
480 0x84, 0x00, // flags, response with authoritative answer
481 0x00, 0x00, // number of questions
482 0x00, 0x01, // number of answer rr
483 0x00, 0x00, // number of name server rr
484 0x00, 0x00, // number of additional rr
485 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
486 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
487 0x00, // null label
488 0x00, 0x01, // type A Record
489 0x00, 0x01, // class IN
490 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
491 0x00, 0x04, // rdlength, 32 bits
492 0xc0, 0xa8, 0x00, 0x01, // 192.168.0.1
493 };
494 patchpanel::DnsResourceRecord answer;
495 answer.name = "www.example.com."; // FQDN with the final dot.
496 answer.type = dns_protocol::kTypeA;
497 answer.klass = dns_protocol::kClassIN;
498 answer.ttl = 120; // 120 seconds.
499 answer.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
500 std::vector<DnsResourceRecord> answers(1, answer);
501 DnsResponse response(0x1234 /* response_id */, true /* is_authoritative*/,
502 answers, {} /* authority_records */,
503 {} /* additional records */, base::nullopt);
504 ASSERT_NE(nullptr, response.io_buffer());
505 EXPECT_TRUE(response.IsValid());
506 std::string expected_response(reinterpret_cast<const char*>(response_data),
507 sizeof(response_data));
508 std::string actual_response(response.io_buffer()->data(),
509 response.io_buffer_size());
510 EXPECT_EQ(expected_response, actual_response);
511}
512
513TEST(DnsResponseWriteTest,
514 SingleAnswerWithQuestionConstructedFromSizeInflatedQuery) {
515 const uint8_t response_data[] = {
516 0x12, 0x34, // ID
517 0x84, 0x00, // flags, response with authoritative answer
518 0x00, 0x01, // number of questions
519 0x00, 0x01, // number of answer rr
520 0x00, 0x00, // number of name server rr
521 0x00, 0x00, // number of additional rr
522 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
523 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
524 0x00, // null label
525 0x00, 0x01, // type A Record
526 0x00, 0x01, // class IN
527 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
528 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
529 0x00, // null label
530 0x00, 0x01, // type A Record
531 0x00, 0x01, // class IN
532 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
533 0x00, 0x04, // rdlength, 32 bits
534 0xc0, 0xa8, 0x00, 0x01, // 192.168.0.1
535 };
536 std::string dotted_name("www.example.com");
537 std::string dns_name;
538 ASSERT_TRUE(DNSDomainFromDot(dotted_name, &dns_name));
539 size_t buf_size =
540 sizeof(dns_protocol::Header) + dns_name.size() + 2 /* qtype */ +
541 2 /* qclass */ +
542 10 /* extra bytes that inflate the internal buffer of a query */;
543 auto buf = base::MakeRefCounted<IOBufferWithSize>(buf_size);
544 memset(buf->data(), 0, buf->size());
545 base::BigEndianWriter writer(buf->data(), buf_size);
546 writer.WriteU16(0x1234); // id
547 writer.WriteU16(0); // flags, is query
548 writer.WriteU16(1); // qdcount
549 writer.WriteU16(0); // ancount
550 writer.WriteU16(0); // nscount
551 writer.WriteU16(0); // arcount
552 writer.WriteBytes(dns_name.data(), dns_name.size()); // qname
553 writer.WriteU16(dns_protocol::kTypeA); // qtype
554 writer.WriteU16(dns_protocol::kClassIN); // qclass
555 // buf contains 10 extra zero bytes.
556 base::Optional<DnsQuery> query;
557 query.emplace(buf);
558 query->Parse(buf_size);
559 patchpanel::DnsResourceRecord answer;
560 answer.name = dotted_name;
561 answer.type = dns_protocol::kTypeA;
562 answer.klass = dns_protocol::kClassIN;
563 answer.ttl = 120; // 120 seconds.
564 answer.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
565 std::vector<DnsResourceRecord> answers(1, answer);
566 DnsResponse response(0x1234 /* id */, true /* is_authoritative*/, answers,
567 {} /* authority_records */, {} /* additional records */,
568 query);
569 ASSERT_NE(nullptr, response.io_buffer());
570 EXPECT_TRUE(response.IsValid());
571 std::string expected_response(reinterpret_cast<const char*>(response_data),
572 sizeof(response_data));
573 std::string actual_response(response.io_buffer()->data(),
574 response.io_buffer_size());
575 EXPECT_EQ(expected_response, actual_response);
576}
577
578TEST(DnsResponseWriteTest, SingleQuadARecordAnswer) {
579 const uint8_t response_data[] = {
580 0x12, 0x34, // ID
581 0x84, 0x00, // flags, response with authoritative answer
582 0x00, 0x00, // number of questions
583 0x00, 0x01, // number of answer rr
584 0x00, 0x00, // number of name server rr
585 0x00, 0x00, // number of additional rr
586 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
587 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
588 0x00, // null label
589 0x00, 0x1c, // type AAAA Record
590 0x00, 0x01, // class IN
591 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
592 0x00, 0x10, // rdlength, 128 bits
593 0xfd, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x01, // fd12:3456:789a:1::1
594 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
595 };
596 patchpanel::DnsResourceRecord answer;
597 answer.name = "www.example.com";
598 answer.type = dns_protocol::kTypeAAAA;
599 answer.klass = dns_protocol::kClassIN;
600 answer.ttl = 120; // 120 seconds.
601 answer.SetOwnedRdata(std::string(
602 "\xfd\x12\x34\x56\x78\x9a\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01", 16));
603 std::vector<DnsResourceRecord> answers(1, answer);
604 DnsResponse response(0x1234 /* id */, true /* is_authoritative*/, answers,
605 {} /* authority_records */, {} /* additional records */,
606 base::nullopt);
607 ASSERT_NE(nullptr, response.io_buffer());
608 EXPECT_TRUE(response.IsValid());
609 std::string expected_response(reinterpret_cast<const char*>(response_data),
610 sizeof(response_data));
611 std::string actual_response(response.io_buffer()->data(),
612 response.io_buffer_size());
613 EXPECT_EQ(expected_response, actual_response);
614}
615
616TEST(DnsResponseWriteTest, TwoAnswersWithAAndQuadARecords) {
617 const uint8_t response_data[] = {
618 0x12, 0x34, // ID
619 0x84, 0x00, // flags, response with authoritative answer
620 0x00, 0x00, // number of questions
621 0x00, 0x02, // number of answer rr
622 0x00, 0x00, // number of name server rr
623 0x00, 0x00, // number of additional rr
624 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
625 0x03, 'c', 'o', 'm',
626 0x00, // null label
627 0x00, 0x01, // type A Record
628 0x00, 0x01, // class IN
629 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
630 0x00, 0x04, // rdlength, 32 bits
631 0xc0, 0xa8, 0x00, 0x01, // 192.168.0.1
632 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0x03, 'o', 'r', 'g',
633 0x00, // null label
634 0x00, 0x1c, // type AAAA Record
635 0x00, 0x01, // class IN
636 0x00, 0x00, 0x00, 0x3c, // TTL, 60 seconds
637 0x00, 0x10, // rdlength, 128 bits
638 0xfd, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x01, // fd12:3456:789a:1::1
639 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
640 };
641 patchpanel::DnsResourceRecord answer1;
642 answer1.name = "www.example.com";
643 answer1.type = dns_protocol::kTypeA;
644 answer1.klass = dns_protocol::kClassIN;
645 answer1.ttl = 120; // 120 seconds.
646 answer1.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
647 patchpanel::DnsResourceRecord answer2;
648 answer2.name = "example.org";
649 answer2.type = dns_protocol::kTypeAAAA;
650 answer2.klass = dns_protocol::kClassIN;
651 answer2.ttl = 60;
652 answer2.SetOwnedRdata(std::string(
653 "\xfd\x12\x34\x56\x78\x9a\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01", 16));
654 std::vector<DnsResourceRecord> answers(2);
655 answers[0] = answer1;
656 answers[1] = answer2;
657 DnsResponse response(0x1234 /* id */, true /* is_authoritative*/, answers,
658 {} /* authority_records */, {} /* additional records */,
659 base::nullopt);
660 ASSERT_NE(nullptr, response.io_buffer());
661 EXPECT_TRUE(response.IsValid());
662 std::string expected_response(reinterpret_cast<const char*>(response_data),
663 sizeof(response_data));
664 std::string actual_response(response.io_buffer()->data(),
665 response.io_buffer_size());
666 EXPECT_EQ(expected_response, actual_response);
667}
668
669TEST(DnsResponseWriteTest, AnswerWithAuthorityRecord) {
670 const uint8_t response_data[] = {
671 0x12, 0x35, // ID
672 0x84, 0x00, // flags, response with authoritative answer
673 0x00, 0x00, // number of questions
674 0x00, 0x00, // number of answer rr
675 0x00, 0x01, // number of name server rr
676 0x00, 0x00, // number of additional rr
677 0x03, 'w', 'w', 'w', 0x07, 'e', 'x', 'a',
678 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm',
679 0x00, // null label
680 0x00, 0x01, // type A Record
681 0x00, 0x01, // class IN
682 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
683 0x00, 0x04, // rdlength, 32 bits
684 0xc0, 0xa8, 0x00, 0x01, // 192.168.0.1
685 };
686 DnsResourceRecord record;
687 record.name = "www.example.com";
688 record.type = dns_protocol::kTypeA;
689 record.klass = dns_protocol::kClassIN;
690 record.ttl = 120; // 120 seconds.
691 record.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
692 std::vector<DnsResourceRecord> authority_records(1, record);
693 DnsResponse response(0x1235 /* response_id */, true /* is_authoritative*/,
694 {} /* answers */, authority_records,
695 {} /* additional records */, base::nullopt);
696 ASSERT_NE(nullptr, response.io_buffer());
697 EXPECT_TRUE(response.IsValid());
698 std::string expected_response(reinterpret_cast<const char*>(response_data),
699 sizeof(response_data));
700 std::string actual_response(response.io_buffer()->data(),
701 response.io_buffer_size());
702 EXPECT_EQ(expected_response, actual_response);
703}
704
705TEST(DnsResponseWriteTest, AnswerWithRcode) {
706 const uint8_t response_data[] = {
707 0x12, 0x12, // ID
708 0x80, 0x03, // flags (response with non-existent domain)
709 0x00, 0x00, // number of questions
710 0x00, 0x00, // number of answer rr
711 0x00, 0x00, // number of name server rr
712 0x00, 0x00, // number of additional rr
713 };
714 DnsResponse response(0x1212 /* response_id */, false /* is_authoritative*/,
715 {} /* answers */, {} /* authority_records */,
716 {} /* additional records */, base::nullopt,
717 dns_protocol::kRcodeNXDOMAIN);
718 ASSERT_NE(nullptr, response.io_buffer());
719 EXPECT_TRUE(response.IsValid());
720 std::string expected_response(reinterpret_cast<const char*>(response_data),
721 sizeof(response_data));
722 std::string actual_response(response.io_buffer()->data(),
723 response.io_buffer_size());
724 EXPECT_EQ(expected_response, actual_response);
725 EXPECT_EQ(dns_protocol::kRcodeNXDOMAIN, response.rcode());
726}
727
728TEST(DnsResponseWriteTest, WrittenResponseCanBeParsed) {
729 std::string dotted_name("www.example.com");
730 patchpanel::DnsResourceRecord answer;
731 answer.name = dotted_name;
732 answer.type = dns_protocol::kTypeA;
733 answer.klass = dns_protocol::kClassIN;
734 answer.ttl = 120; // 120 seconds.
735 answer.SetOwnedRdata(std::string("\xc0\xa8\x00\x01", 4));
736 std::vector<DnsResourceRecord> answers(1, answer);
737 patchpanel::DnsResourceRecord additional_record;
738 additional_record.name = dotted_name;
739 additional_record.type = dns_protocol::kTypeNSEC;
740 additional_record.klass = dns_protocol::kClassIN;
741 additional_record.ttl = 120; // 120 seconds.
742 additional_record.SetOwnedRdata(std::string("\xc0\x0c\x00\x01\x04", 5));
743 std::vector<DnsResourceRecord> additional_records(1, additional_record);
744 DnsResponse response(0x1234 /* response_id */, true /* is_authoritative*/,
745 answers, {} /* authority_records */, additional_records,
746 base::nullopt);
747 ASSERT_NE(nullptr, response.io_buffer());
748 EXPECT_TRUE(response.IsValid());
749 EXPECT_THAT(response.id(), testing::Optional(0x1234));
750 EXPECT_EQ(1u, response.answer_count());
751 EXPECT_EQ(1u, response.additional_answer_count());
752 auto parser = response.Parser();
753 patchpanel::DnsResourceRecord parsed_record;
754 EXPECT_TRUE(parser.ReadRecord(&parsed_record));
755 // Answer with an A record.
756 EXPECT_EQ(answer.name, parsed_record.name);
757 EXPECT_EQ(answer.type, parsed_record.type);
758 EXPECT_EQ(answer.klass, parsed_record.klass);
759 EXPECT_EQ(answer.ttl, parsed_record.ttl);
760 EXPECT_EQ(answer.owned_rdata, parsed_record.rdata);
761 // Additional NSEC record.
762 EXPECT_TRUE(parser.ReadRecord(&parsed_record));
763 EXPECT_EQ(additional_record.name, parsed_record.name);
764 EXPECT_EQ(additional_record.type, parsed_record.type);
765 EXPECT_EQ(additional_record.klass, parsed_record.klass);
766 EXPECT_EQ(additional_record.ttl, parsed_record.ttl);
767 EXPECT_EQ(additional_record.owned_rdata, parsed_record.rdata);
768}
769
770} // namespace
771
772} // namespace patchpanel