OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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 "components/certificate_transparency/log_dns_client.h" | 5 #include "components/certificate_transparency/log_dns_client.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" |
8 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
9 #include "base/location.h" | 10 #include "base/location.h" |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
12 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
13 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
15 #include "base/threading/thread_task_runner_handle.h" | 16 #include "base/threading/thread_task_runner_handle.h" |
16 #include "base/time/time.h" | 17 #include "base/time/time.h" |
17 #include "components/base32/base32.h" | 18 #include "components/base32/base32.h" |
18 #include "crypto/sha2.h" | 19 #include "crypto/sha2.h" |
19 #include "net/base/net_errors.h" | |
20 #include "net/cert/merkle_audit_proof.h" | 20 #include "net/cert/merkle_audit_proof.h" |
21 #include "net/dns/dns_client.h" | 21 #include "net/dns/dns_client.h" |
22 #include "net/dns/dns_config_service.h" | 22 #include "net/dns/dns_config_service.h" |
23 #include "net/dns/dns_protocol.h" | 23 #include "net/dns/dns_protocol.h" |
24 #include "net/dns/dns_response.h" | 24 #include "net/dns/dns_response.h" |
25 #include "net/dns/dns_transaction.h" | 25 #include "net/dns/dns_transaction.h" |
26 #include "net/dns/record_parsed.h" | 26 #include "net/dns/record_parsed.h" |
27 #include "net/dns/record_rdata.h" | 27 #include "net/dns/record_rdata.h" |
28 | 28 |
29 namespace certificate_transparency { | 29 namespace certificate_transparency { |
30 | 30 |
31 namespace { | 31 namespace { |
32 | 32 |
33 // Parses the DNS response and extracts a single string from the TXT RDATA. | 33 // Parses the DNS response and extracts a single string from the TXT RDATA. |
34 // If the response is malformed, not a TXT record, or contains any number of | 34 // If the response is malformed, not a TXT record, or contains any number of |
35 // strings other than 1, this returns false and extracts nothing. | 35 // strings other than 1, this returns false and extracts nothing. |
36 // Otherwise, it returns true and the extracted string is assigned to |*txt|. | 36 // Otherwise, it returns true and the extracted string is assigned to |*txt|. |
37 bool ParseTxtResponse(const net::DnsResponse& response, std::string* txt) { | 37 bool ParseTxtResponse(const net::DnsResponse& response, std::string* txt) { |
38 DCHECK(txt); | 38 DCHECK(txt); |
39 | 39 |
40 net::DnsRecordParser parser = response.Parser(); | 40 net::DnsRecordParser parser = response.Parser(); |
41 // We don't care about the creation time, since we're going to throw | 41 // We don't care about the creation time, since we're going to throw |
42 // |parsed_record| away as soon as we've extracted the payload, so provide | 42 // |parsed_record| away as soon as we've extracted the payload, so provide |
43 // the "null" time. | 43 // the "null" time. |
44 auto parsed_record = net::RecordParsed::CreateFrom(&parser, base::Time()); | 44 auto parsed_record = net::RecordParsed::CreateFrom(&parser, base::Time()); |
45 if (parsed_record == nullptr) | 45 if (!parsed_record) |
46 return false; | 46 return false; |
47 | 47 |
48 auto* txt_record = parsed_record->rdata<net::TxtRecordRdata>(); | 48 auto* txt_record = parsed_record->rdata<net::TxtRecordRdata>(); |
49 if (txt_record == nullptr) | 49 if (!txt_record) |
50 return false; | 50 return false; |
51 | 51 |
52 // The draft CT-over-DNS RFC says that there MUST be exactly one string in the | 52 // The draft CT-over-DNS RFC says that there MUST be exactly one string in the |
53 // TXT record. | 53 // TXT record. |
54 if (txt_record->texts().size() != 1) | 54 if (txt_record->texts().size() != 1) |
55 return false; | 55 return false; |
56 | 56 |
57 *txt = txt_record->texts().front(); | 57 *txt = txt_record->texts().front(); |
58 return true; | 58 return true; |
59 } | 59 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 return true; | 96 return true; |
97 } | 97 } |
98 | 98 |
99 } // namespace | 99 } // namespace |
100 | 100 |
101 // Encapsulates the state machine required to get an audit proof from a Merkle | 101 // Encapsulates the state machine required to get an audit proof from a Merkle |
102 // leaf hash. This requires a DNS request to obtain the leaf index, then a | 102 // leaf hash. This requires a DNS request to obtain the leaf index, then a |
103 // series of DNS requests to get the nodes of the proof. | 103 // series of DNS requests to get the nodes of the proof. |
104 class LogDnsClient::AuditProofQuery { | 104 class LogDnsClient::AuditProofQuery { |
105 public: | 105 public: |
106 using CompletionCallback = | |
107 base::Callback<void(int net_error, AuditProofQuery* query)>; | |
108 | |
109 // The LogDnsClient is guaranteed to outlive the AuditProofQuery, so it's safe | 106 // The LogDnsClient is guaranteed to outlive the AuditProofQuery, so it's safe |
110 // to leave ownership of |dns_client| with LogDnsClient. | 107 // to leave ownership of |dns_client| with LogDnsClient. |
111 AuditProofQuery(net::DnsClient* dns_client, | 108 AuditProofQuery(net::DnsClient* dns_client, |
112 const std::string& domain_for_log, | 109 const std::string& domain_for_log, |
113 uint64_t tree_size, | 110 uint64_t tree_size, |
114 const net::NetLogWithSource& net_log); | 111 const net::NetLogWithSource& net_log); |
115 | 112 |
116 // Begins the process of getting an audit proof for the CT log entry with a | 113 // Begins the process of getting an audit |proof| for the CT log entry with a |
117 // leaf hash of |leaf_hash|. The |callback| will be invoked when finished. | 114 // leaf hash of |leaf_hash|. If it cannot be obtained synchronously, |
118 void Start(base::StringPiece leaf_hash, CompletionCallback callback); | 115 // net::ERR_IO_PENDING will be returned and |callback| will be invoked when |
119 | 116 // the operation has completed asynchronously. |
120 // Transfers the audit proof to the caller. | 117 // Ownership of |proof| remains with the caller, and it must not be deleted |
121 // Only call this once the query has completed, otherwise the proof will be | 118 // until the operation is complete. |
122 // incomplete. | 119 net::Error Start(std::string leaf_hash, |
123 std::unique_ptr<net::ct::MerkleAuditProof> TakeProof(); | 120 const net::CompletionCallback& callback, |
| 121 net::ct::MerkleAuditProof* proof); |
124 | 122 |
125 private: | 123 private: |
126 // Requests the leaf index of the CT log entry with |leaf_hash|. | 124 enum class State { |
127 void QueryLeafIndex(base::StringPiece leaf_hash); | 125 NONE, |
128 | 126 REQUEST_LEAF_INDEX, |
129 // Processes the response to a leaf index request. | 127 REQUEST_LEAF_INDEX_COMPLETE, |
130 // The received leaf index will be added to the proof. | 128 REQUEST_AUDIT_PROOF_NODES, |
131 void QueryLeafIndexComplete(net::DnsTransaction* transaction, | 129 REQUEST_AUDIT_PROOF_NODES_COMPLETE, |
132 int net_error, | 130 }; |
133 const net::DnsResponse* response); | 131 |
134 | 132 net::Error DoLoop(net::Error result); |
135 // Queries a CT log to retrieve part of an audit proof. The |node_index| | 133 |
136 // indicates which node of the audit proof/ should be requested. The CT log | 134 // When a DnsTransaction completes, store the response and resume the state |
137 // may return up to 7 nodes, starting from |node_index| (this is the maximum | 135 // machine. It is safe to store a pointer to |response| because |transaction| |
138 // that will fit in a DNS UDP packet). The nodes will be appended to the | 136 // is kept alive in |current_dns_transaction_|. |
139 // proof. | 137 void OnDnsTransactionComplete(net::DnsTransaction* transaction, |
140 void QueryAuditProofNodes(uint64_t node_index); | 138 int net_error, |
141 | 139 const net::DnsResponse* response); |
142 // Processes the response to an audit proof request. | 140 |
143 // This will contain some, but not necessarily all, of the audit proof nodes. | 141 // Requests the leaf index for the CT log entry with |leaf_hash_|. |
144 void QueryAuditProofNodesComplete(net::DnsTransaction* transaction, | 142 net::Error RequestLeafIndex(); |
145 int net_error, | 143 |
146 const net::DnsResponse* response); | 144 // Stores the received leaf index in |proof_->leaf_index|. |
147 | 145 // If successful, the audit proof nodes will be requested next. |
| 146 net::Error RequestLeafIndexComplete(net::Error result); |
| 147 |
| 148 // Requests the next batch of audit proof nodes from a CT log. |
| 149 // The index of the first node required is determined by looking at how many |
| 150 // nodes are already in |proof_->nodes|. |
| 151 // The CT log may return up to 7 nodes - this is the maximum allowed by the |
| 152 // CT-over-DNS draft RFC, as a TXT RDATA string can have a maximum length of |
| 153 // 255 bytes and each node is 32 bytes long (a SHA-256 hash). |
| 154 // |
| 155 // The performance of this could be improved by sending all of the expected |
| 156 // requests up front. Each response can contain a maximum of 7 audit path |
| 157 // nodes, so for an audit proof of size 20, it could send 3 queries (for nodes |
| 158 // 0-6, 7-13 and 14-19) immediately. Currently, it sends only the first and |
| 159 // then, based on the number of nodes received, sends the next query. |
| 160 // The complexity of the code would increase though, as it would need to |
| 161 // detect gaps in the audit proof caused by the server not responding with the |
| 162 // anticipated number of nodes. It would also undermine LogDnsClient's ability |
| 163 // to rate-limit DNS requests. |
| 164 net::Error RequestAuditProofNodes(); |
| 165 |
| 166 // Appends the received audit proof nodes to |proof_->nodes|. |
| 167 // If any nodes are missing, another request will follow this one. |
| 168 net::Error RequestAuditProofNodesComplete(net::Error result); |
| 169 |
| 170 // Sends a TXT record request for the domain |qname|. |
| 171 // Returns true if the request could be started. |
| 172 // OnDnsTransactionComplete() will be invoked with the result of the request. |
| 173 bool StartDnsTransaction(const std::string& qname); |
| 174 |
| 175 // The next state that this query will enter. |
| 176 State next_state_; |
| 177 // The DNS domain of the CT log that is being queried. |
148 std::string domain_for_log_; | 178 std::string domain_for_log_; |
| 179 // The Merkle leaf hash of the CT log entry an audit proof is required for. |
| 180 std::string leaf_hash_; |
| 181 // The size of the CT log's tree, from which the proof is requested. |
149 // TODO(robpercival): Remove |tree_size| once |proof_| has a tree_size member. | 182 // TODO(robpercival): Remove |tree_size| once |proof_| has a tree_size member. |
150 uint64_t tree_size_; | 183 uint64_t tree_size_; |
151 std::unique_ptr<net::ct::MerkleAuditProof> proof_; | 184 // The audit proof to populate. |
152 CompletionCallback callback_; | 185 net::ct::MerkleAuditProof* proof_; |
| 186 // The callback to invoke when the query is complete. |
| 187 net::CompletionCallback callback_; |
| 188 // The DnsClient to use for sending DNS requests to the CT log. |
153 net::DnsClient* dns_client_; | 189 net::DnsClient* dns_client_; |
| 190 // The most recent DNS request. Null if no DNS requests have been made. |
154 std::unique_ptr<net::DnsTransaction> current_dns_transaction_; | 191 std::unique_ptr<net::DnsTransaction> current_dns_transaction_; |
| 192 // The most recent DNS response. Only valid so long as the corresponding DNS |
| 193 // request is stored in |current_dns_transaction_|. |
| 194 const net::DnsResponse* last_dns_response_; |
| 195 // The NetLog that DNS transactions will log to. |
155 net::NetLogWithSource net_log_; | 196 net::NetLogWithSource net_log_; |
| 197 // Produces WeakPtrs to |this| for binding callbacks. |
156 base::WeakPtrFactory<AuditProofQuery> weak_ptr_factory_; | 198 base::WeakPtrFactory<AuditProofQuery> weak_ptr_factory_; |
157 }; | 199 }; |
158 | 200 |
159 LogDnsClient::AuditProofQuery::AuditProofQuery( | 201 LogDnsClient::AuditProofQuery::AuditProofQuery( |
160 net::DnsClient* dns_client, | 202 net::DnsClient* dns_client, |
161 const std::string& domain_for_log, | 203 const std::string& domain_for_log, |
162 uint64_t tree_size, | 204 uint64_t tree_size, |
163 const net::NetLogWithSource& net_log) | 205 const net::NetLogWithSource& net_log) |
164 : domain_for_log_(domain_for_log), | 206 : next_state_(State::NONE), |
| 207 domain_for_log_(domain_for_log), |
165 tree_size_(tree_size), | 208 tree_size_(tree_size), |
166 dns_client_(dns_client), | 209 dns_client_(dns_client), |
167 net_log_(net_log), | 210 net_log_(net_log), |
168 weak_ptr_factory_(this) { | 211 weak_ptr_factory_(this) { |
169 DCHECK(dns_client_); | 212 DCHECK(dns_client_); |
170 DCHECK(!domain_for_log_.empty()); | 213 DCHECK(!domain_for_log_.empty()); |
171 } | 214 } |
172 | 215 |
173 void LogDnsClient::AuditProofQuery::Start(base::StringPiece leaf_hash, | 216 // |leaf_hash| is not a const-ref to allow callers to std::move that string into |
174 CompletionCallback callback) { | 217 // the method, avoiding the need to make a copy. |
175 current_dns_transaction_.reset(); | 218 net::Error LogDnsClient::AuditProofQuery::Start( |
176 proof_ = base::MakeUnique<net::ct::MerkleAuditProof>(); | 219 std::string leaf_hash, |
| 220 const net::CompletionCallback& callback, |
| 221 net::ct::MerkleAuditProof* proof) { |
| 222 // It should not already be in progress. |
| 223 DCHECK_EQ(State::NONE, next_state_); |
| 224 proof_ = proof; |
| 225 leaf_hash_ = std::move(leaf_hash); |
177 callback_ = callback; | 226 callback_ = callback; |
178 QueryLeafIndex(leaf_hash); | 227 // The first step in the query is to request the leaf index corresponding to |
179 } | 228 // |leaf_hash| from the CT log. |
180 | 229 next_state_ = State::REQUEST_LEAF_INDEX; |
181 std::unique_ptr<net::ct::MerkleAuditProof> | 230 // Begin the state machine. |
182 LogDnsClient::AuditProofQuery::TakeProof() { | 231 return DoLoop(net::OK); |
183 return std::move(proof_); | 232 } |
184 } | 233 |
185 | 234 net::Error LogDnsClient::AuditProofQuery::DoLoop(net::Error result) { |
186 void LogDnsClient::AuditProofQuery::QueryLeafIndex( | 235 CHECK_NE(State::NONE, next_state_); |
187 base::StringPiece leaf_hash) { | 236 do { |
188 std::string encoded_leaf_hash = | 237 State state = next_state_; |
189 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING); | 238 next_state_ = State::NONE; |
190 DCHECK_EQ(encoded_leaf_hash.size(), 52u); | 239 switch (state) { |
191 | 240 case State::REQUEST_LEAF_INDEX: |
192 std::string qname = base::StringPrintf( | 241 result = RequestLeafIndex(); |
193 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log_.data()); | 242 break; |
194 | 243 case State::REQUEST_LEAF_INDEX_COMPLETE: |
195 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | 244 result = RequestLeafIndexComplete(result); |
196 if (factory == nullptr) { | 245 break; |
197 base::ThreadTaskRunnerHandle::Get()->PostTask( | 246 case State::REQUEST_AUDIT_PROOF_NODES: |
198 FROM_HERE, base::Bind(callback_, net::Error::ERR_NAME_RESOLUTION_FAILED, | 247 result = RequestAuditProofNodes(); |
199 base::Unretained(this))); | 248 break; |
200 return; | 249 case State::REQUEST_AUDIT_PROOF_NODES_COMPLETE: |
201 } | 250 result = RequestAuditProofNodesComplete(result); |
202 | 251 break; |
203 net::DnsTransactionFactory::CallbackType transaction_callback = | 252 case State::NONE: |
204 base::Bind(&LogDnsClient::AuditProofQuery::QueryLeafIndexComplete, | 253 NOTREACHED(); |
205 weak_ptr_factory_.GetWeakPtr()); | 254 break; |
206 | 255 } |
207 current_dns_transaction_ = factory->CreateTransaction( | 256 } while (result != net::ERR_IO_PENDING && next_state_ != State::NONE); |
208 qname, net::dns_protocol::kTypeTXT, transaction_callback, net_log_); | 257 |
209 | 258 return result; |
210 current_dns_transaction_->Start(); | 259 } |
211 } | 260 |
212 | 261 void LogDnsClient::AuditProofQuery::OnDnsTransactionComplete( |
213 void LogDnsClient::AuditProofQuery::QueryLeafIndexComplete( | |
214 net::DnsTransaction* transaction, | 262 net::DnsTransaction* transaction, |
215 int net_error, | 263 int net_error, |
216 const net::DnsResponse* response) { | 264 const net::DnsResponse* response) { |
217 // If we've received no response but no net::error either (shouldn't | 265 DCHECK_EQ(current_dns_transaction_.get(), transaction); |
218 // happen), | 266 last_dns_response_ = response; |
219 // report the response as invalid. | 267 net::Error result = DoLoop(static_cast<net::Error>(net_error)); |
220 if (response == nullptr && net_error == net::OK) { | 268 |
221 net_error = net::ERR_INVALID_RESPONSE; | 269 // If DoLoop() indicates that I/O is pending, don't invoke the completion |
222 } | 270 // callback. OnDnsTransactionComplete() will be invoked again once the I/O |
223 | 271 // is complete, and can invoke the completion callback then if appropriate. |
224 if (net_error != net::OK) { | 272 if (result != net::ERR_IO_PENDING) { |
225 base::ThreadTaskRunnerHandle::Get()->PostTask( | 273 // The callback will delete this query (now that it has finished), so copy |
226 FROM_HERE, base::Bind(callback_, net_error, base::Unretained(this))); | 274 // |callback_| before running it so that it is not deleted along with the |
227 return; | 275 // query, mid-callback-execution (which would result in a crash). |
228 } | 276 base::ResetAndReturn(&callback_).Run(result); |
229 | 277 } |
230 if (!ParseLeafIndex(*response, &proof_->leaf_index)) { | 278 } |
231 base::ThreadTaskRunnerHandle::Get()->PostTask( | 279 |
232 FROM_HERE, base::Bind(callback_, net::ERR_DNS_MALFORMED_RESPONSE, | 280 net::Error LogDnsClient::AuditProofQuery::RequestLeafIndex() { |
233 base::Unretained(this))); | 281 std::string encoded_leaf_hash = base32::Base32Encode( |
234 return; | 282 leaf_hash_, base32::Base32EncodePolicy::OMIT_PADDING); |
| 283 DCHECK_EQ(encoded_leaf_hash.size(), 52u); |
| 284 |
| 285 std::string qname = base::StringPrintf( |
| 286 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log_.c_str()); |
| 287 |
| 288 if (!StartDnsTransaction(qname)) { |
| 289 return net::ERR_NAME_RESOLUTION_FAILED; |
| 290 } |
| 291 |
| 292 next_state_ = State::REQUEST_LEAF_INDEX_COMPLETE; |
| 293 return net::ERR_IO_PENDING; |
| 294 } |
| 295 |
| 296 // Stores the received leaf index in |proof_->leaf_index|. |
| 297 // If successful, the audit proof nodes will be requested next. |
| 298 net::Error LogDnsClient::AuditProofQuery::RequestLeafIndexComplete( |
| 299 net::Error result) { |
| 300 if (result != net::OK) { |
| 301 return result; |
| 302 } |
| 303 |
| 304 DCHECK(last_dns_response_); |
| 305 if (!ParseLeafIndex(*last_dns_response_, &proof_->leaf_index)) { |
| 306 return net::ERR_DNS_MALFORMED_RESPONSE; |
235 } | 307 } |
236 | 308 |
237 // Reject leaf index if it is out-of-range. | 309 // Reject leaf index if it is out-of-range. |
238 // This indicates either: | 310 // This indicates either: |
239 // a) the wrong tree_size was provided. | 311 // a) the wrong tree_size was provided. |
240 // b) the wrong leaf hash was provided. | 312 // b) the wrong leaf hash was provided. |
241 // c) there is a bug server-side. | 313 // c) there is a bug server-side. |
242 // The first two are more likely, so return ERR_INVALID_ARGUMENT. | 314 // The first two are more likely, so return ERR_INVALID_ARGUMENT. |
243 if (proof_->leaf_index >= tree_size_) { | 315 if (proof_->leaf_index >= tree_size_) { |
244 base::ThreadTaskRunnerHandle::Get()->PostTask( | 316 return net::ERR_INVALID_ARGUMENT; |
245 FROM_HERE, base::Bind(callback_, net::ERR_INVALID_ARGUMENT, | 317 } |
246 base::Unretained(this))); | 318 |
247 return; | 319 next_state_ = State::REQUEST_AUDIT_PROOF_NODES; |
248 } | 320 return net::OK; |
249 | 321 } |
250 // QueryAuditProof for the first batch of audit proof_ nodes (i.e. starting | 322 |
251 // from 0). | 323 net::Error LogDnsClient::AuditProofQuery::RequestAuditProofNodes() { |
252 QueryAuditProofNodes(0 /* start node index */); | 324 // Test pre-conditions (should be guaranteed by DNS response validation). |
253 } | 325 if (proof_->leaf_index >= tree_size_ || |
254 | 326 proof_->nodes.size() >= |
255 void LogDnsClient::AuditProofQuery::QueryAuditProofNodes(uint64_t node_index) { | 327 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_)) { |
256 DCHECK_LT(proof_->leaf_index, tree_size_); | 328 return net::ERR_UNEXPECTED; |
257 DCHECK_LT(node_index, | 329 } |
258 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_)); | |
259 | 330 |
260 std::string qname = base::StringPrintf( | 331 std::string qname = base::StringPrintf( |
261 "%" PRIu64 ".%" PRIu64 ".%" PRIu64 ".tree.%s.", node_index, | 332 "%zu.%" PRIu64 ".%" PRIu64 ".tree.%s.", proof_->nodes.size(), |
262 proof_->leaf_index, tree_size_, domain_for_log_.data()); | 333 proof_->leaf_index, tree_size_, domain_for_log_.c_str()); |
263 | 334 |
264 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | 335 if (!StartDnsTransaction(qname)) { |
265 if (factory == nullptr) { | 336 return net::ERR_NAME_RESOLUTION_FAILED; |
266 base::ThreadTaskRunnerHandle::Get()->PostTask( | 337 } |
267 FROM_HERE, base::Bind(callback_, net::Error::ERR_NAME_RESOLUTION_FAILED, | 338 |
268 base::Unretained(this))); | 339 next_state_ = State::REQUEST_AUDIT_PROOF_NODES_COMPLETE; |
269 return; | 340 return net::ERR_IO_PENDING; |
270 } | 341 } |
271 | 342 |
272 net::DnsTransactionFactory::CallbackType transaction_callback = | 343 net::Error LogDnsClient::AuditProofQuery::RequestAuditProofNodesComplete( |
273 base::Bind(&LogDnsClient::AuditProofQuery::QueryAuditProofNodesComplete, | 344 net::Error result) { |
274 weak_ptr_factory_.GetWeakPtr()); | 345 if (result != net::OK) { |
275 | 346 return result; |
276 current_dns_transaction_ = factory->CreateTransaction( | |
277 qname, net::dns_protocol::kTypeTXT, transaction_callback, net_log_); | |
278 current_dns_transaction_->Start(); | |
279 } | |
280 | |
281 void LogDnsClient::AuditProofQuery::QueryAuditProofNodesComplete( | |
282 net::DnsTransaction* transaction, | |
283 int net_error, | |
284 const net::DnsResponse* response) { | |
285 // If we receive no response but no net::error either (shouldn't happen), | |
286 // report the response as invalid. | |
287 if (response == nullptr && net_error == net::OK) { | |
288 net_error = net::ERR_INVALID_RESPONSE; | |
289 } | |
290 | |
291 if (net_error != net::OK) { | |
292 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
293 FROM_HERE, base::Bind(callback_, net_error, base::Unretained(this))); | |
294 return; | |
295 } | 347 } |
296 | 348 |
297 const uint64_t audit_path_length = | 349 const uint64_t audit_path_length = |
298 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_); | 350 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_); |
| 351 |
299 // The calculated |audit_path_length| can't ever be greater than 64, so | 352 // The calculated |audit_path_length| can't ever be greater than 64, so |
300 // deriving the amount of memory to reserve from the untrusted |leaf_index| | 353 // deriving the amount of memory to reserve from the untrusted |leaf_index| |
301 // is safe. | 354 // is safe. |
302 proof_->nodes.reserve(audit_path_length); | 355 proof_->nodes.reserve(audit_path_length); |
303 | 356 |
304 if (!ParseAuditPath(*response, proof_.get())) { | 357 DCHECK(last_dns_response_); |
305 base::ThreadTaskRunnerHandle::Get()->PostTask( | 358 if (!ParseAuditPath(*last_dns_response_, proof_)) { |
306 FROM_HERE, base::Bind(callback_, net::ERR_DNS_MALFORMED_RESPONSE, | 359 return net::ERR_DNS_MALFORMED_RESPONSE; |
307 base::Unretained(this))); | 360 } |
308 return; | 361 |
309 } | 362 // Keep requesting more proof nodes until all of them are received. |
310 | 363 if (proof_->nodes.size() < audit_path_length) { |
311 const uint64_t audit_path_nodes_received = proof_->nodes.size(); | 364 next_state_ = State::REQUEST_AUDIT_PROOF_NODES; |
312 if (audit_path_nodes_received < audit_path_length) { | 365 } |
313 QueryAuditProofNodes(audit_path_nodes_received); | 366 |
314 return; | 367 return net::OK; |
315 } | 368 } |
316 | 369 |
317 base::ThreadTaskRunnerHandle::Get()->PostTask( | 370 bool LogDnsClient::AuditProofQuery::StartDnsTransaction( |
318 FROM_HERE, base::Bind(callback_, net::OK, base::Unretained(this))); | 371 const std::string& qname) { |
| 372 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); |
| 373 if (!factory) { |
| 374 return false; |
| 375 } |
| 376 |
| 377 current_dns_transaction_ = factory->CreateTransaction( |
| 378 qname, net::dns_protocol::kTypeTXT, |
| 379 base::Bind(&LogDnsClient::AuditProofQuery::OnDnsTransactionComplete, |
| 380 weak_ptr_factory_.GetWeakPtr()), |
| 381 net_log_); |
| 382 |
| 383 current_dns_transaction_->Start(); |
| 384 return true; |
319 } | 385 } |
320 | 386 |
321 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, | 387 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, |
322 const net::NetLogWithSource& net_log, | 388 const net::NetLogWithSource& net_log, |
323 size_t max_concurrent_queries) | 389 size_t max_concurrent_queries) |
324 : dns_client_(std::move(dns_client)), | 390 : dns_client_(std::move(dns_client)), |
325 net_log_(net_log), | 391 net_log_(net_log), |
326 max_concurrent_queries_(max_concurrent_queries), | 392 max_concurrent_queries_(max_concurrent_queries), |
327 weak_ptr_factory_(this) { | 393 weak_ptr_factory_(this) { |
328 CHECK(dns_client_); | 394 CHECK(dns_client_); |
329 net::NetworkChangeNotifier::AddDNSObserver(this); | 395 net::NetworkChangeNotifier::AddDNSObserver(this); |
330 UpdateDnsConfig(); | 396 UpdateDnsConfig(); |
331 } | 397 } |
332 | 398 |
333 LogDnsClient::~LogDnsClient() { | 399 LogDnsClient::~LogDnsClient() { |
334 net::NetworkChangeNotifier::RemoveDNSObserver(this); | 400 net::NetworkChangeNotifier::RemoveDNSObserver(this); |
335 } | 401 } |
336 | 402 |
337 void LogDnsClient::OnDNSChanged() { | 403 void LogDnsClient::OnDNSChanged() { |
338 UpdateDnsConfig(); | 404 UpdateDnsConfig(); |
339 } | 405 } |
340 | 406 |
341 void LogDnsClient::OnInitialDNSConfigRead() { | 407 void LogDnsClient::OnInitialDNSConfigRead() { |
342 UpdateDnsConfig(); | 408 UpdateDnsConfig(); |
343 } | 409 } |
344 | 410 |
345 // The performance of this could be improved by sending all of the expected | 411 // |leaf_hash| is not a const-ref to allow callers to std::move that string into |
346 // queries up front. Each response can contain a maximum of 7 audit path nodes, | 412 // the method, avoiding LogDnsClient::AuditProofQuery having to make a copy. |
347 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6, | 413 net::Error LogDnsClient::QueryAuditProof( |
348 // 7-13 and 14-19) immediately. Currently, it sends only the first and then, | 414 base::StringPiece domain_for_log, |
349 // based on the number of nodes received, sends the next query. The complexity | 415 std::string leaf_hash, |
350 // of the code would increase though, as it would need to detect gaps in the | 416 uint64_t tree_size, |
351 // audit proof caused by the server not responding with the anticipated number | 417 net::ct::MerkleAuditProof* proof, |
352 // of nodes. Ownership of the proof would need to change, as it would be shared | 418 const net::CompletionCallback& callback) { |
353 // between simultaneous DNS transactions. Throttling of queries would also need | 419 DCHECK(proof); |
354 // to take into account this increase in parallelism. | 420 |
355 void LogDnsClient::QueryAuditProof(const std::string& domain_for_log, | |
356 base::StringPiece leaf_hash, | |
357 uint64_t tree_size, | |
358 const AuditProofCallback& callback) { | |
359 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { | 421 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { |
360 base::ThreadTaskRunnerHandle::Get()->PostTask( | 422 return net::ERR_INVALID_ARGUMENT; |
361 FROM_HERE, | |
362 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr)); | |
363 return; | |
364 } | 423 } |
365 | 424 |
366 if (HasMaxConcurrentQueriesInProgress()) { | 425 if (HasMaxConcurrentQueriesInProgress()) { |
367 base::ThreadTaskRunnerHandle::Get()->PostTask( | 426 return net::ERR_TEMPORARILY_THROTTLED; |
368 FROM_HERE, | |
369 base::Bind(callback, net::Error::ERR_TEMPORARILY_THROTTLED, nullptr)); | |
370 return; | |
371 } | 427 } |
372 | 428 |
373 audit_proof_queries_.emplace_back(new AuditProofQuery( | 429 AuditProofQuery* query = new AuditProofQuery( |
374 dns_client_.get(), domain_for_log, tree_size, net_log_)); | 430 dns_client_.get(), domain_for_log.as_string(), tree_size, net_log_); |
| 431 // Transfers ownership of |query| to |audit_proof_queries_|. |
| 432 audit_proof_queries_.emplace_back(query); |
375 | 433 |
376 AuditProofQuery::CompletionCallback internal_callback = | 434 return query->Start(std::move(leaf_hash), |
377 base::Bind(&LogDnsClient::QueryAuditProofComplete, | 435 base::Bind(&LogDnsClient::QueryAuditProofComplete, |
378 weak_ptr_factory_.GetWeakPtr(), callback); | 436 weak_ptr_factory_.GetWeakPtr(), |
379 | 437 base::Unretained(query), callback), |
380 audit_proof_queries_.back()->Start(leaf_hash, internal_callback); | 438 proof); |
381 } | 439 } |
382 | 440 |
383 void LogDnsClient::QueryAuditProofComplete(const AuditProofCallback& callback, | 441 void LogDnsClient::QueryAuditProofComplete( |
384 int result, | 442 AuditProofQuery* query, |
385 AuditProofQuery* query) { | 443 const net::CompletionCallback& callback, |
| 444 int net_error) { |
386 DCHECK(query); | 445 DCHECK(query); |
387 | 446 |
388 std::unique_ptr<net::ct::MerkleAuditProof> proof; | |
389 if (result == net::OK) { | |
390 proof = query->TakeProof(); | |
391 } | |
392 | |
393 // Finished with the query - destroy it. | 447 // Finished with the query - destroy it. |
394 auto query_iterator = | 448 auto query_iterator = |
395 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), | 449 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), |
396 [query](const std::unique_ptr<AuditProofQuery>& p) { | 450 [query](const std::unique_ptr<AuditProofQuery>& p) { |
397 return p.get() == query; | 451 return p.get() == query; |
398 }); | 452 }); |
399 DCHECK(query_iterator != audit_proof_queries_.end()); | 453 DCHECK(query_iterator != audit_proof_queries_.end()); |
400 audit_proof_queries_.erase(query_iterator); | 454 audit_proof_queries_.erase(query_iterator); |
401 | 455 |
402 base::ThreadTaskRunnerHandle::Get()->PostTask( | 456 callback.Run(net_error); |
403 FROM_HERE, base::Bind(callback, result, base::Passed(&proof))); | |
404 } | 457 } |
405 | 458 |
406 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { | 459 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { |
407 return max_concurrent_queries_ != 0 && | 460 return max_concurrent_queries_ != 0 && |
408 audit_proof_queries_.size() >= max_concurrent_queries_; | 461 audit_proof_queries_.size() >= max_concurrent_queries_; |
409 } | 462 } |
410 | 463 |
411 void LogDnsClient::UpdateDnsConfig() { | 464 void LogDnsClient::UpdateDnsConfig() { |
412 net::DnsConfig config; | 465 net::DnsConfig config; |
413 net::NetworkChangeNotifier::GetDnsConfig(&config); | 466 net::NetworkChangeNotifier::GetDnsConfig(&config); |
414 if (config.IsValid()) | 467 if (config.IsValid()) |
415 dns_client_->SetConfig(config); | 468 dns_client_->SetConfig(config); |
416 } | 469 } |
417 | 470 |
418 } // namespace certificate_transparency | 471 } // namespace certificate_transparency |
OLD | NEW |