OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium 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 "components/certificate_transparency/mock_log_dns_traffic.h" | |
6 | |
7 #include <algorithm> | |
8 #include <numeric> | |
9 #include <vector> | |
10 | |
11 #include "base/big_endian.h" | |
12 #include "base/sys_byteorder.h" | |
13 #include "base/test/test_timeouts.h" | |
14 #include "net/dns/dns_client.h" | |
15 #include "net/dns/dns_protocol.h" | |
16 #include "net/dns/dns_util.h" | |
17 #include "net/socket/socket_test_util.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace certificate_transparency { | |
21 | |
22 namespace { | |
23 | |
24 // Always return min, to simplify testing. | |
25 // This should result in the DNS query ID always being 0. | |
26 int FakeRandInt(int min, int max) { | |
27 return min; | |
28 } | |
29 | |
30 std::vector<char> CreateDnsTxtRequest(base::StringPiece qname) { | |
31 std::string encoded_qname; | |
32 EXPECT_TRUE(net::DNSDomainFromDot(qname, &encoded_qname)); | |
33 | |
34 const size_t query_section_size = encoded_qname.size() + 4; | |
Eran Messeri
2016/07/21 14:47:02
What does the '4' here stand for? Would it make se
Rob Percival
2016/07/21 17:32:21
It's the 4 bytes you see written below, just after
Eran Messeri
2016/07/22 15:55:48
Splitting sounds better.
| |
35 | |
36 std::vector<char> request(sizeof(net::dns_protocol::Header) + | |
37 query_section_size); | |
38 base::BigEndianWriter writer(request.data(), request.size()); | |
39 | |
40 // Header | |
41 net::dns_protocol::Header header = {}; | |
42 header.flags = base::HostToNet16(net::dns_protocol::kFlagRD); | |
43 header.qdcount = base::HostToNet16(1); | |
44 EXPECT_TRUE(writer.WriteBytes(&header, sizeof(header))); | |
45 // Query section | |
46 EXPECT_TRUE(writer.WriteBytes(encoded_qname.data(), encoded_qname.size())); | |
47 EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT)); | |
48 EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN)); | |
49 EXPECT_EQ(0, writer.remaining()); | |
50 | |
51 return request; | |
52 } | |
53 | |
54 std::vector<char> CreateDnsTxtResponse(const std::vector<char>& request, | |
55 base::StringPiece answer) { | |
56 const size_t answers_section_size = 12 + answer.size(); | |
Eran Messeri
2016/07/21 14:47:02
Another magic number, please have a constant expla
Rob Percival
2016/07/21 17:32:21
Again, this is the 12 bytes you see written a litt
Eran Messeri
2016/07/22 15:55:48
Again, breaking down seems better as it's clear wh
| |
57 constexpr uint32_t ttl = 86400; // seconds | |
58 | |
59 std::vector<char> response(request.size() + answers_section_size); | |
60 std::copy(request.begin(), request.end(), response.begin()); | |
61 // Modify the header | |
62 net::dns_protocol::Header* header = | |
63 reinterpret_cast<net::dns_protocol::Header*>(response.data()); | |
64 header->ancount = base::HostToNet16(1); | |
65 header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse); | |
66 | |
67 // Write the answer section | |
68 base::BigEndianWriter writer(response.data() + request.size(), | |
69 response.size() - request.size()); | |
70 EXPECT_TRUE(writer.WriteU8(0xc0)); // qname is a pointer | |
71 EXPECT_TRUE(writer.WriteU8( | |
72 sizeof(*header))); // address of qname (start of query section) | |
73 EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT)); | |
74 EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN)); | |
75 EXPECT_TRUE(writer.WriteU32(ttl)); | |
76 EXPECT_TRUE(writer.WriteU16(answer.size())); | |
77 EXPECT_TRUE(writer.WriteBytes(answer.data(), answer.size())); | |
78 EXPECT_EQ(0, writer.remaining()); | |
79 | |
80 return response; | |
81 } | |
82 | |
83 std::vector<char> CreateDnsErrorResponse(const std::vector<char>& request, | |
84 uint8_t rcode) { | |
85 std::vector<char> response(request); | |
86 // Modify the header | |
87 net::dns_protocol::Header* header = | |
88 reinterpret_cast<net::dns_protocol::Header*>(response.data()); | |
89 header->ancount = base::HostToNet16(1); | |
90 header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse | rcode); | |
91 | |
92 return response; | |
93 } | |
94 | |
95 } // namespace | |
96 | |
97 namespace detail { | |
98 | |
99 MockSocketData::MockSocketData(const std::vector<char>& write, | |
100 const std::vector<char>& read) | |
101 : expected_write_payload_(write), | |
102 expected_read_payload_(read), | |
103 expected_write_(net::SYNCHRONOUS, | |
104 expected_write_payload_.data(), | |
105 expected_write_payload_.size(), | |
106 0), | |
107 expected_reads_{net::MockRead(net::ASYNC, | |
108 expected_read_payload_.data(), | |
109 expected_read_payload_.size(), | |
110 1), | |
111 eof_}, | |
112 socket_data_(expected_reads_, 2, &expected_write_, 1) {} | |
113 | |
114 MockSocketData::MockSocketData(const std::vector<char>& write, int net_error) | |
115 : expected_write_payload_(write), | |
116 expected_write_(net::SYNCHRONOUS, | |
117 expected_write_payload_.data(), | |
118 expected_write_payload_.size(), | |
119 0), | |
120 expected_reads_{net::MockRead(net::ASYNC, net_error, 1), eof_}, | |
121 socket_data_(expected_reads_, 2, &expected_write_, 1) {} | |
122 | |
123 MockSocketData::MockSocketData(const std::vector<char>& write) | |
124 : expected_write_payload_(write), | |
125 expected_write_(net::SYNCHRONOUS, | |
126 expected_write_payload_.data(), | |
127 expected_write_payload_.size(), | |
128 0), | |
129 expected_reads_{net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING, 1), | |
130 eof_}, | |
131 socket_data_(expected_reads_, 2, &expected_write_, 1) {} | |
132 | |
133 MockSocketData::~MockSocketData() {} | |
134 | |
135 void MockSocketData::AddToFactory( | |
136 net::MockClientSocketFactory* socket_factory) { | |
137 socket_factory->AddSocketDataProvider(&socket_data_); | |
138 } | |
139 | |
140 const net::MockRead MockSocketData::eof_(net::SYNCHRONOUS, | |
141 net::ERR_IO_PENDING, | |
142 2); | |
143 | |
144 } // namespace detail | |
145 | |
146 using detail::MockSocketData; | |
147 | |
148 MockLogDnsTraffic::MockLogDnsTraffic() : socket_read_mode_(net::ASYNC) { | |
149 // Use an invalid nameserver address. This prevents the tests accidentally | |
150 // sending real DNS queries. The mock sockets don't care that the address | |
151 // is invalid. | |
152 dns_config_.nameservers.push_back(net::IPEndPoint()); | |
153 // Don't attempt retransmissions - just fail. | |
154 dns_config_.attempts = 1; | |
155 // This ensures timeouts are long enough for memory tests. | |
156 dns_config_.timeout = TestTimeouts::action_timeout(); | |
157 // Simplify testing - don't require random numbers for the source port. | |
158 // This means our FakeRandInt function should only be called to get query | |
159 // IDs. | |
160 dns_config_.randomize_ports = false; | |
161 } | |
162 | |
163 MockLogDnsTraffic::~MockLogDnsTraffic() {} | |
164 | |
165 void MockLogDnsTraffic::ExpectRequestAndErrorResponse(base::StringPiece qname, | |
166 uint8_t rcode) { | |
167 std::vector<char> request = CreateDnsTxtRequest(qname); | |
168 std::vector<char> response = CreateDnsErrorResponse(request, rcode); | |
169 | |
170 mock_socket_data_.emplace_back(new MockSocketData(request, response)); | |
Eran Messeri
2016/07/21 14:47:02
inline request and response, since they're not use
Rob Percival
2016/07/21 17:32:21
Creating |response| requires |request|, so they ca
Eran Messeri
2016/07/22 15:55:48
So at the very least you could inline creating the
| |
171 mock_socket_data_.back()->SetReadMode(socket_read_mode_); | |
172 mock_socket_data_.back()->AddToFactory(&socket_factory_); | |
173 } | |
174 | |
175 void MockLogDnsTraffic::ExpectRequestAndSocketError(base::StringPiece qname, | |
176 int net_error) { | |
177 std::vector<char> request = CreateDnsTxtRequest(qname); | |
178 | |
179 mock_socket_data_.emplace_back(new MockSocketData(request, net_error)); | |
Eran Messeri
2016/07/21 14:47:02
Seems like you could have a helper method/function
Rob Percival
2016/07/21 17:32:21
Done.
| |
180 mock_socket_data_.back()->SetReadMode(socket_read_mode_); | |
181 mock_socket_data_.back()->AddToFactory(&socket_factory_); | |
182 } | |
183 | |
184 void MockLogDnsTraffic::ExpectRequestAndTimeout(base::StringPiece qname) { | |
185 std::vector<char> request = CreateDnsTxtRequest(qname); | |
186 | |
187 mock_socket_data_.emplace_back(new MockSocketData(request)); | |
188 mock_socket_data_.back()->SetReadMode(socket_read_mode_); | |
189 mock_socket_data_.back()->AddToFactory(&socket_factory_); | |
190 | |
191 // Speed up timeout tests. | |
192 dns_config_.timeout = TestTimeouts::tiny_timeout(); | |
193 } | |
194 | |
195 void MockLogDnsTraffic::ExpectLeafIndexRequestAndResponse( | |
196 base::StringPiece qname, | |
197 base::StringPiece leaf_index) { | |
198 // Prepend size to leaf_index to create the query answer (rdata) | |
199 ASSERT_LE(leaf_index.size(), 0xFFul); // size must fit into a single byte | |
200 std::string answer = leaf_index.as_string(); | |
201 answer.insert(answer.begin(), static_cast<char>(leaf_index.size())); | |
202 | |
203 ExpectRequestAndResponse(qname, answer); | |
204 } | |
205 | |
206 void MockLogDnsTraffic::ExpectAuditProofRequestAndResponse( | |
207 base::StringPiece qname, | |
208 std::vector<std::string>::const_iterator audit_path_start, | |
209 std::vector<std::string>::const_iterator audit_path_end) { | |
210 // Join nodes in the audit path into a single string. | |
211 std::string proof = | |
212 std::accumulate(audit_path_start, audit_path_end, std::string()); | |
213 | |
214 // Prepend size to proof to create the query answer (rdata) | |
215 ASSERT_LE(proof.size(), 0xFFul); // size must fit into a single byte | |
216 proof.insert(proof.begin(), static_cast<char>(proof.size())); | |
217 | |
218 ExpectRequestAndResponse(qname, proof); | |
219 } | |
220 | |
221 std::unique_ptr<net::DnsClient> MockLogDnsTraffic::CreateDnsClient() { | |
222 std::unique_ptr<net::DnsClient> client = | |
223 net::DnsClient::CreateClientForTesting(nullptr, &socket_factory_, | |
224 base::Bind(&FakeRandInt)); | |
225 client->SetConfig(dns_config_); | |
226 return client; | |
227 } | |
228 | |
229 void MockLogDnsTraffic::ExpectRequestAndResponse(base::StringPiece qname, | |
230 base::StringPiece answer) { | |
231 std::vector<char> request = CreateDnsTxtRequest(qname); | |
232 std::vector<char> response = CreateDnsTxtResponse(request, answer); | |
233 | |
234 mock_socket_data_.emplace_back(new MockSocketData(request, response)); | |
235 mock_socket_data_.back()->SetReadMode(socket_read_mode_); | |
236 mock_socket_data_.back()->AddToFactory(&socket_factory_); | |
237 } | |
238 | |
239 } // namespace certificate_transparency | |
OLD | NEW |