Chromium Code Reviews| 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/format_macros.h" | 8 #include "base/format_macros.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
| 12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 13 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
| 14 #include "base/threading/thread_task_runner_handle.h" | 14 #include "base/threading/thread_task_runner_handle.h" |
| 15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
| 16 #include "components/base32/base32.h" | 16 #include "components/base32/base32.h" |
| 17 #include "crypto/sha2.h" | 17 #include "crypto/sha2.h" |
| 18 #include "net/base/net_errors.h" | |
| 19 #include "net/cert/merkle_audit_proof.h" | 18 #include "net/cert/merkle_audit_proof.h" |
| 20 #include "net/dns/dns_client.h" | 19 #include "net/dns/dns_client.h" |
| 21 #include "net/dns/dns_config_service.h" | 20 #include "net/dns/dns_config_service.h" |
| 22 #include "net/dns/dns_protocol.h" | 21 #include "net/dns/dns_protocol.h" |
| 23 #include "net/dns/dns_response.h" | 22 #include "net/dns/dns_response.h" |
| 24 #include "net/dns/dns_transaction.h" | 23 #include "net/dns/dns_transaction.h" |
| 25 #include "net/dns/record_parsed.h" | 24 #include "net/dns/record_parsed.h" |
| 26 #include "net/dns/record_rdata.h" | 25 #include "net/dns/record_rdata.h" |
| 27 | 26 |
| 28 namespace certificate_transparency { | 27 namespace certificate_transparency { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 80 } | 79 } |
| 81 | 80 |
| 82 } // namespace | 81 } // namespace |
| 83 | 82 |
| 84 // Encapsulates the state machine required to get an audit proof from a Merkle | 83 // Encapsulates the state machine required to get an audit proof from a Merkle |
| 85 // leaf hash. This requires a DNS request to obtain the leaf index, then a | 84 // leaf hash. This requires a DNS request to obtain the leaf index, then a |
| 86 // series of DNS requests to get the nodes of the proof. | 85 // series of DNS requests to get the nodes of the proof. |
| 87 class LogDnsClient::AuditProofQuery { | 86 class LogDnsClient::AuditProofQuery { |
| 88 public: | 87 public: |
| 89 using CompletionCallback = | 88 using CompletionCallback = |
| 90 base::Callback<void(int net_error, AuditProofQuery* query)>; | 89 base::Callback<void(net::Error result, AuditProofQuery* query)>; |
| 91 | 90 |
| 92 // The LogDnsClient is guaranteed to outlive the AuditProofQuery, so it's safe | 91 // The LogDnsClient is guaranteed to outlive the AuditProofQuery, so it's safe |
| 93 // to leave ownership of |dns_client| with LogDnsClient. | 92 // to leave ownership of |dns_client| with LogDnsClient. |
| 94 AuditProofQuery(net::DnsClient* dns_client, | 93 AuditProofQuery(net::DnsClient* dns_client, |
|
Ryan Sleevi
2016/10/03 23:38:47
The choice to integrate both the definition and th
Rob Percival
2016/10/04 18:35:39
Done.
| |
| 95 const std::string& domain_for_log, | 94 const std::string& domain_for_log, |
| 96 uint64_t tree_size, | 95 uint64_t tree_size, |
| 97 const net::NetLogWithSource& net_log) | 96 const net::NetLogWithSource& net_log) |
| 98 : domain_for_log_(domain_for_log), | 97 : next_state_(State::NONE), |
| 98 domain_for_log_(domain_for_log), | |
| 99 tree_size_(tree_size), | 99 tree_size_(tree_size), |
| 100 dns_client_(dns_client), | 100 dns_client_(dns_client), |
| 101 net_log_(net_log), | 101 net_log_(net_log), |
| 102 weak_ptr_factory_(this) { | 102 weak_ptr_factory_(this) { |
| 103 DCHECK(dns_client_); | 103 DCHECK(dns_client_); |
| 104 DCHECK(!domain_for_log_.empty()); | 104 DCHECK(!domain_for_log_.empty()); |
| 105 } | 105 } |
| 106 | 106 |
| 107 // Start the query. | 107 // Start the query. |
| 108 void Start(base::StringPiece leaf_hash, CompletionCallback callback) { | 108 net::Error Start(base::StringPiece leaf_hash, CompletionCallback callback) { |
| 109 current_dns_transaction_.reset(); | 109 // It should not already be in progress. |
| 110 DCHECK_EQ(State::NONE, next_state_); | |
| 111 // Start a new audit proof. | |
|
Ryan Sleevi
2016/10/03 23:38:47
I appreciate the commenting, but this one seems ov
Rob Percival
2016/10/04 18:35:39
Done.
| |
| 110 proof_.reset(new net::ct::MerkleAuditProof); | 112 proof_.reset(new net::ct::MerkleAuditProof); |
| 113 // The leaf hash will be used during the REQUEST_LEAF_INDEX state. | |
|
Ryan Sleevi
2016/10/03 23:38:47
I appreciate the commenting, but this one seems ov
Rob Percival
2016/10/04 18:35:40
Done.
| |
| 114 leaf_hash.CopyToString(&leaf_hash_); | |
| 115 // The callback will be used during the REQUEST_AUDIT_PROOF_NODES_COMPLETE | |
| 116 // state. | |
|
Ryan Sleevi
2016/10/03 23:38:47
I appreciate the commenting, but this one seems ov
Rob Percival
2016/10/04 18:35:40
Done.
| |
| 111 callback_ = callback; | 117 callback_ = callback; |
| 112 QueryLeafIndex(leaf_hash); | 118 // The first step in the query is to request the leaf index corresponding to |
| 113 } | 119 // |leaf_hash| from the CT log. |
| 114 | 120 next_state_ = State::REQUEST_LEAF_INDEX; |
| 121 // Begin the state machine. | |
| 122 return DoLoop(net::OK); | |
| 123 } | |
| 124 | |
| 125 // Take the audit proof obtained by the query. | |
| 126 // Should only be called once the CompletionCallback is invoked. | |
| 115 std::unique_ptr<net::ct::MerkleAuditProof> TakeProof() { | 127 std::unique_ptr<net::ct::MerkleAuditProof> TakeProof() { |
| 116 return std::move(proof_); | 128 return std::move(proof_); |
| 117 } | 129 } |
| 118 | 130 |
| 119 private: | 131 private: |
| 120 void QueryLeafIndex(base::StringPiece leaf_hash) { | 132 enum class State { |
|
Ryan Sleevi
2016/10/03 23:38:47
I believe this may be the first use of enum class
Rob Percival
2016/10/04 18:35:39
Looks like it's been done a few times before (http
| |
| 133 NONE, | |
| 134 REQUEST_LEAF_INDEX, | |
| 135 REQUEST_LEAF_INDEX_COMPLETE, | |
| 136 REQUEST_AUDIT_PROOF_NODES, | |
| 137 REQUEST_AUDIT_PROOF_NODES_COMPLETE, | |
| 138 }; | |
| 139 | |
| 140 net::Error DoLoop(net::Error result) { | |
| 141 CHECK_NE(State::NONE, next_state_); | |
| 142 do { | |
| 143 State state = next_state_; | |
| 144 next_state_ = State::NONE; | |
| 145 switch (state) { | |
| 146 case State::REQUEST_LEAF_INDEX: | |
| 147 result = RequestLeafIndex(); | |
| 148 break; | |
| 149 case State::REQUEST_LEAF_INDEX_COMPLETE: | |
| 150 result = RequestLeafIndexComplete(result); | |
| 151 break; | |
| 152 case State::REQUEST_AUDIT_PROOF_NODES: | |
| 153 result = RequestAuditProofNodes(); | |
| 154 break; | |
| 155 case State::REQUEST_AUDIT_PROOF_NODES_COMPLETE: | |
| 156 result = RequestAuditProofNodesComplete(result); | |
| 157 break; | |
| 158 case State::NONE: | |
| 159 NOTREACHED(); | |
| 160 break; | |
| 161 } | |
| 162 } while (result != net::ERR_IO_PENDING && next_state_ != State::NONE); | |
| 163 | |
| 164 return result; | |
| 165 } | |
| 166 | |
| 167 // When a DnsTransaction completes, store the response and resume the state | |
| 168 // machine. It is safe to store a pointer to |response| because |transaction| | |
| 169 // is kept alive in |current_dns_transaction_|. | |
| 170 void OnDnsTransactionComplete(net::DnsTransaction* transaction, | |
| 171 int net_error, | |
| 172 const net::DnsResponse* response) { | |
| 173 DCHECK_EQ(current_dns_transaction_.get(), transaction); | |
|
Ryan Sleevi
2016/10/03 23:38:47
Is the .get() necessary? operator== is supposed to
Rob Percival
2016/10/04 18:35:39
Yes, as unique_ptr doesn't have an operator== over
| |
| 174 last_dns_response_ = response; | |
| 175 net::Error result = DoLoop(static_cast<net::Error>(net_error)); | |
| 176 | |
| 177 // If DoLoop() indicates that I/O is pending, don't invoke the completion | |
| 178 // callback. OnDnsTransactionComplete() will be invoked again once the I/O | |
| 179 // is complete, and can invoke the completion callback then if appropriate. | |
| 180 if (result != net::ERR_IO_PENDING) { | |
| 181 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 182 FROM_HERE, base::Bind(callback_, result, base::Unretained(this))); | |
|
Ryan Sleevi
2016/10/03 23:38:47
Why do you PostTask yield here? This is generally
Rob Percival
2016/10/04 18:35:39
It's safe because the callback will only be invoke
| |
| 183 } | |
| 184 } | |
| 185 | |
| 186 // Requests the leaf index for the CT log entry with |leaf_hash_|. | |
| 187 net::Error RequestLeafIndex() { | |
| 121 std::string encoded_leaf_hash = base32::Base32Encode( | 188 std::string encoded_leaf_hash = base32::Base32Encode( |
| 122 leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING); | 189 leaf_hash_, base32::Base32EncodePolicy::OMIT_PADDING); |
| 123 DCHECK_EQ(encoded_leaf_hash.size(), 52u); | 190 DCHECK_EQ(encoded_leaf_hash.size(), 52u); |
| 124 | 191 |
| 125 std::string qname = base::StringPrintf( | 192 std::string qname = base::StringPrintf( |
| 126 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log_.data()); | 193 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log_.c_str()); |
| 127 | 194 |
| 128 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | 195 if (!StartDnsTransaction(qname)) { |
| 129 if (factory == nullptr) { | 196 return net::ERR_NAME_RESOLUTION_FAILED; |
| 130 base::ThreadTaskRunnerHandle::Get()->PostTask( | 197 } |
| 131 FROM_HERE, | 198 |
| 132 base::Bind(callback_, net::Error::ERR_NAME_RESOLUTION_FAILED, | 199 next_state_ = State::REQUEST_LEAF_INDEX_COMPLETE; |
| 133 base::Unretained(this))); | 200 return net::ERR_IO_PENDING; |
| 134 return; | 201 } |
| 135 } | 202 |
| 136 | 203 // Stores the received leaf index in |proof_->leaf_index|. |
| 137 net::DnsTransactionFactory::CallbackType transaction_callback = | 204 // If successful, the audit proof nodes will be requested next. |
| 138 base::Bind(&LogDnsClient::AuditProofQuery::QueryLeafIndexComplete, | 205 net::Error RequestLeafIndexComplete(net::Error result) { |
| 139 weak_ptr_factory_.GetWeakPtr()); | 206 if (result != net::OK) { |
| 140 | 207 return result; |
| 141 current_dns_transaction_ = factory->CreateTransaction( | 208 } |
| 142 qname, net::dns_protocol::kTypeTXT, transaction_callback, net_log_); | 209 |
| 143 | 210 DCHECK(last_dns_response_); |
| 144 current_dns_transaction_->Start(); | 211 if (!ParseLeafIndex(*last_dns_response_, &proof_->leaf_index)) { |
| 145 } | 212 return net::ERR_DNS_MALFORMED_RESPONSE; |
| 146 | |
| 147 void QueryLeafIndexComplete(net::DnsTransaction* transaction, | |
| 148 int net_error, | |
| 149 const net::DnsResponse* response) { | |
| 150 // If we've received no response but no net::error either (shouldn't | |
| 151 // happen), | |
| 152 // report the response as invalid. | |
| 153 if (response == nullptr && net_error == net::OK) { | |
| 154 net_error = net::ERR_INVALID_RESPONSE; | |
| 155 } | |
| 156 | |
| 157 if (net_error != net::OK) { | |
| 158 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 159 FROM_HERE, base::Bind(callback_, net_error, base::Unretained(this))); | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 if (!ParseLeafIndex(*response, &proof_->leaf_index)) { | |
| 164 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 165 FROM_HERE, base::Bind(callback_, net::ERR_DNS_MALFORMED_RESPONSE, | |
| 166 base::Unretained(this))); | |
| 167 return; | |
| 168 } | 213 } |
| 169 | 214 |
| 170 // Reject leaf index if it is out-of-range. | 215 // Reject leaf index if it is out-of-range. |
| 171 // This indicates either: | 216 // This indicates either: |
| 172 // a) the wrong tree_size was provided. | 217 // a) the wrong tree_size was provided. |
| 173 // b) the wrong leaf hash was provided. | 218 // b) the wrong leaf hash was provided. |
| 174 // c) there is a bug server-side. | 219 // c) there is a bug server-side. |
| 175 // The first two are more likely, so return ERR_INVALID_ARGUMENT. | 220 // The first two are more likely, so return ERR_INVALID_ARGUMENT. |
| 176 if (proof_->leaf_index >= tree_size_) { | 221 if (proof_->leaf_index >= tree_size_) { |
| 177 base::ThreadTaskRunnerHandle::Get()->PostTask( | 222 return net::ERR_INVALID_ARGUMENT; |
| 178 FROM_HERE, base::Bind(callback_, net::ERR_INVALID_ARGUMENT, | 223 } |
| 179 base::Unretained(this))); | 224 |
| 180 return; | 225 next_state_ = State::REQUEST_AUDIT_PROOF_NODES; |
| 181 } | 226 return net::OK; |
| 182 | 227 } |
| 183 // QueryAuditProof for the first batch of audit proof_ nodes (i.e. starting | 228 |
| 184 // from 0). | 229 // Requests the next batch of audit proof nodes from a CT log. |
| 185 QueryAuditProofNodes(0 /* start node index */); | 230 // The index of the first node required is determined by looking at how many |
| 186 } | 231 // nodes are already in |proof_->nodes|. |
| 187 | 232 // The CT log may return up to 7 nodes - this is the maximum allowed by the |
| 188 // Queries a CT log to retrieve part of an audit |proof|. The |node_index| | 233 // CT-over-DNS draft RFC, as a TXT RDATA string can have a maximum length of |
| 189 // indicates which node of the audit proof/ should be requested. The CT log | 234 // 255 bytes and each node is 32 bytes long (a SHA-256 hash). |
| 190 // may return up to 7 nodes, starting from |node_index| (this is the maximum | 235 // |
| 191 // that will fit in a DNS UDP packet). The nodes will be appended to | 236 // The performance of this could be improved by sending all of the expected |
| 192 // |proof->nodes|. | 237 // requests up front. Each response can contain a maximum of 7 audit path |
| 193 void QueryAuditProofNodes(uint64_t node_index) { | 238 // nodes, so for an audit proof of size 20, it could send 3 queries (for nodes |
| 239 // 0-6, 7-13 and 14-19) immediately. Currently, it sends only the first and | |
| 240 // then, based on the number of nodes received, sends the next query. | |
| 241 // The complexity of the code would increase though, as it would need to | |
| 242 // detect gaps in the audit proof caused by the server not responding with the | |
| 243 // anticipated number of nodes. It would also undermine LogDnsClient's ability | |
| 244 // to rate-limit DNS requests. | |
|
Ryan Sleevi
2016/10/03 23:38:47
This last comment is not obvious about why this is
Rob Percival
2016/10/04 18:35:40
If AuditProofQuery could perform parallel DNS requ
| |
| 245 net::Error RequestAuditProofNodes() { | |
| 194 DCHECK_LT(proof_->leaf_index, tree_size_); | 246 DCHECK_LT(proof_->leaf_index, tree_size_); |
| 195 DCHECK_LT(node_index, net::ct::CalculateAuditPathLength(proof_->leaf_index, | 247 DCHECK_LT(proof_->nodes.size(), net::ct::CalculateAuditPathLength( |
| 196 tree_size_)); | 248 proof_->leaf_index, tree_size_)); |
|
Ryan Sleevi
2016/10/03 23:38:47
Are these DCHECKs guarding against programmer erro
Rob Percival
2016/10/04 18:35:40
These variables are validated as soon as they are
| |
| 197 | 249 |
| 198 std::string qname = base::StringPrintf( | 250 std::string qname = base::StringPrintf( |
| 199 "%" PRIu64 ".%" PRIu64 ".%" PRIu64 ".tree.%s.", node_index, | 251 "%zu.%" PRIu64 ".%" PRIu64 ".tree.%s.", proof_->nodes.size(), |
| 200 proof_->leaf_index, tree_size_, domain_for_log_.data()); | 252 proof_->leaf_index, tree_size_, domain_for_log_.c_str()); |
| 201 | 253 |
| 202 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | 254 if (!StartDnsTransaction(qname)) { |
| 203 if (factory == nullptr) { | 255 return net::ERR_NAME_RESOLUTION_FAILED; |
| 204 base::ThreadTaskRunnerHandle::Get()->PostTask( | 256 } |
| 205 FROM_HERE, | 257 |
| 206 base::Bind(callback_, net::Error::ERR_NAME_RESOLUTION_FAILED, | 258 next_state_ = State::REQUEST_AUDIT_PROOF_NODES_COMPLETE; |
| 207 base::Unretained(this))); | 259 return net::ERR_IO_PENDING; |
| 208 return; | 260 } |
| 209 } | 261 |
| 210 | 262 // Appends the received audit proof nodes to |proof_->nodes|. |
| 211 net::DnsTransactionFactory::CallbackType transaction_callback = | 263 // If any nodes are missing, another request will follow this one. |
| 212 base::Bind(&LogDnsClient::AuditProofQuery::QueryAuditProofNodesComplete, | 264 net::Error RequestAuditProofNodesComplete(net::Error result) { |
| 213 weak_ptr_factory_.GetWeakPtr()); | 265 if (result != net::OK) { |
| 214 | 266 return result; |
| 215 current_dns_transaction_ = factory->CreateTransaction( | |
| 216 qname, net::dns_protocol::kTypeTXT, transaction_callback, net_log_); | |
| 217 current_dns_transaction_->Start(); | |
| 218 } | |
| 219 | |
| 220 void QueryAuditProofNodesComplete(net::DnsTransaction* transaction, | |
| 221 int net_error, | |
| 222 const net::DnsResponse* response) { | |
| 223 // If we receive no response but no net::error either (shouldn't happen), | |
| 224 // report the response as invalid. | |
| 225 if (response == nullptr && net_error == net::OK) { | |
| 226 net_error = net::ERR_INVALID_RESPONSE; | |
| 227 } | |
| 228 | |
| 229 if (net_error != net::OK) { | |
| 230 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 231 FROM_HERE, base::Bind(callback_, net_error, base::Unretained(this))); | |
| 232 return; | |
| 233 } | 267 } |
| 234 | 268 |
| 235 const uint64_t audit_path_length = | 269 const uint64_t audit_path_length = |
| 236 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_); | 270 net::ct::CalculateAuditPathLength(proof_->leaf_index, tree_size_); |
| 271 | |
| 237 // The calculated |audit_path_length| can't ever be greater than 64, so | 272 // The calculated |audit_path_length| can't ever be greater than 64, so |
| 238 // deriving the amount of memory to reserve from the untrusted |leaf_index| | 273 // deriving the amount of memory to reserve from the untrusted |leaf_index| |
| 239 // is safe. | 274 // is safe. |
| 240 proof_->nodes.reserve(audit_path_length); | 275 proof_->nodes.reserve(audit_path_length); |
| 241 | 276 |
| 242 if (!ParseAuditPath(*response, proof_.get())) { | 277 DCHECK(last_dns_response_); |
| 243 base::ThreadTaskRunnerHandle::Get()->PostTask( | 278 if (!ParseAuditPath(*last_dns_response_, proof_.get())) { |
| 244 FROM_HERE, base::Bind(callback_, net::ERR_DNS_MALFORMED_RESPONSE, | 279 return net::ERR_DNS_MALFORMED_RESPONSE; |
| 245 base::Unretained(this))); | 280 } |
| 246 return; | 281 |
| 247 } | 282 // If we don't have all of the proof nodes yet, request more. |
|
Ryan Sleevi
2016/10/03 23:38:47
// Keep requesting more proof nodes until all of t
Rob Percival
2016/10/04 18:35:39
Done.
| |
| 248 | 283 if (proof_->nodes.size() < audit_path_length) { |
| 249 const uint64_t audit_path_nodes_received = proof_->nodes.size(); | 284 next_state_ = State::REQUEST_AUDIT_PROOF_NODES; |
| 250 if (audit_path_nodes_received < audit_path_length) { | 285 } |
| 251 QueryAuditProofNodes(audit_path_nodes_received); | 286 |
| 252 return; | 287 return net::OK; |
| 253 } | 288 } |
| 254 | 289 |
| 255 base::ThreadTaskRunnerHandle::Get()->PostTask( | 290 bool StartDnsTransaction(const std::string& qname) { |
| 256 FROM_HERE, base::Bind(callback_, net::OK, base::Unretained(this))); | 291 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); |
| 257 } | 292 if (factory == nullptr) { |
| 258 | 293 return false; |
| 294 } | |
| 295 | |
| 296 current_dns_transaction_ = factory->CreateTransaction( | |
| 297 qname, net::dns_protocol::kTypeTXT, | |
| 298 base::Bind(&LogDnsClient::AuditProofQuery::OnDnsTransactionComplete, | |
| 299 weak_ptr_factory_.GetWeakPtr()), | |
| 300 net_log_); | |
| 301 | |
| 302 current_dns_transaction_->Start(); | |
| 303 return true; | |
| 304 } | |
| 305 | |
| 306 // The next state that this query will enter. | |
| 307 State next_state_; | |
| 308 // The DNS domain of the CT log that is being queried. | |
| 259 std::string domain_for_log_; | 309 std::string domain_for_log_; |
| 310 // The Merkle leaf hash of the CT log entry an audit proof is required for. | |
| 311 std::string leaf_hash_; | |
| 312 // The size of the CT log's tree, from which the proof is requested. | |
| 260 // TODO(robpercival): Remove |tree_size| once |proof_| has a tree_size member. | 313 // TODO(robpercival): Remove |tree_size| once |proof_| has a tree_size member. |
| 261 uint64_t tree_size_; | 314 uint64_t tree_size_; |
| 315 // The audit proof. It will be null until the query is started and incomplete | |
| 316 // until the query is finished. | |
| 262 std::unique_ptr<net::ct::MerkleAuditProof> proof_; | 317 std::unique_ptr<net::ct::MerkleAuditProof> proof_; |
| 318 // The callback to invoke when the query is complete. | |
| 263 CompletionCallback callback_; | 319 CompletionCallback callback_; |
| 320 // The DnsClient to use for sending DNS requests to the CT log. | |
| 264 net::DnsClient* dns_client_; | 321 net::DnsClient* dns_client_; |
| 322 // The most recent DNS request. Null if no DNS requests have been made. | |
| 265 std::unique_ptr<net::DnsTransaction> current_dns_transaction_; | 323 std::unique_ptr<net::DnsTransaction> current_dns_transaction_; |
| 324 // The most recent DNS response. Only valid so long as the corresponding DNS | |
| 325 // request is stored in |current_dns_transaction_|. | |
| 326 const net::DnsResponse* last_dns_response_; | |
| 327 // The NetLog that DNS transactions will log to. | |
| 266 net::NetLogWithSource net_log_; | 328 net::NetLogWithSource net_log_; |
| 329 // Produces WeakPtrs to |this| for binding callbacks. | |
| 267 base::WeakPtrFactory<AuditProofQuery> weak_ptr_factory_; | 330 base::WeakPtrFactory<AuditProofQuery> weak_ptr_factory_; |
| 268 }; | 331 }; |
| 269 | 332 |
| 270 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, | 333 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, |
| 271 const net::NetLogWithSource& net_log, | 334 const net::NetLogWithSource& net_log, |
| 272 size_t max_concurrent_queries) | 335 size_t max_concurrent_queries) |
| 273 : dns_client_(std::move(dns_client)), | 336 : dns_client_(std::move(dns_client)), |
| 274 net_log_(net_log), | 337 net_log_(net_log), |
| 275 max_concurrent_queries_(max_concurrent_queries), | 338 max_concurrent_queries_(max_concurrent_queries), |
| 276 weak_ptr_factory_(this) { | 339 weak_ptr_factory_(this) { |
| 277 CHECK(dns_client_); | 340 CHECK(dns_client_); |
| 278 net::NetworkChangeNotifier::AddDNSObserver(this); | 341 net::NetworkChangeNotifier::AddDNSObserver(this); |
| 279 UpdateDnsConfig(); | 342 UpdateDnsConfig(); |
| 280 } | 343 } |
| 281 | 344 |
| 282 LogDnsClient::~LogDnsClient() { | 345 LogDnsClient::~LogDnsClient() { |
| 283 net::NetworkChangeNotifier::RemoveDNSObserver(this); | 346 net::NetworkChangeNotifier::RemoveDNSObserver(this); |
| 284 } | 347 } |
| 285 | 348 |
| 286 void LogDnsClient::OnDNSChanged() { | 349 void LogDnsClient::OnDNSChanged() { |
| 287 UpdateDnsConfig(); | 350 UpdateDnsConfig(); |
| 288 } | 351 } |
| 289 | 352 |
| 290 void LogDnsClient::OnInitialDNSConfigRead() { | 353 void LogDnsClient::OnInitialDNSConfigRead() { |
| 291 UpdateDnsConfig(); | 354 UpdateDnsConfig(); |
| 292 } | 355 } |
| 293 | 356 |
| 294 // The performance of this could be improved by sending all of the expected | 357 net::Error LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log, |
| 295 // queries up front. Each response can contain a maximum of 7 audit path nodes, | 358 base::StringPiece leaf_hash, |
| 296 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6, | 359 uint64_t tree_size, |
| 297 // 7-13 and 14-19) immediately. Currently, it sends only the first and then, | 360 const AuditProofCallback& callback) { |
| 298 // based on the number of nodes received, sends the next query. The complexity | |
| 299 // of the code would increase though, as it would need to detect gaps in the | |
| 300 // audit proof caused by the server not responding with the anticipated number | |
| 301 // of nodes. Ownership of the proof would need to change, as it would be shared | |
| 302 // between simultaneous DNS transactions. Throttling of queries would also need | |
| 303 // to take into account this increase in parallelism. | |
| 304 void LogDnsClient::QueryAuditProof(const std::string& domain_for_log, | |
| 305 base::StringPiece leaf_hash, | |
| 306 uint64_t tree_size, | |
| 307 const AuditProofCallback& callback) { | |
| 308 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { | 361 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { |
| 309 base::ThreadTaskRunnerHandle::Get()->PostTask( | 362 return net::ERR_INVALID_ARGUMENT; |
| 310 FROM_HERE, | |
| 311 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr)); | |
| 312 return; | |
| 313 } | 363 } |
| 314 | 364 |
| 315 if (HasMaxConcurrentQueriesInProgress()) { | 365 if (HasMaxConcurrentQueriesInProgress()) { |
| 316 base::ThreadTaskRunnerHandle::Get()->PostTask( | 366 return net::ERR_TEMPORARILY_THROTTLED; |
| 317 FROM_HERE, | |
| 318 base::Bind(callback, net::Error::ERR_TEMPORARILY_THROTTLED, nullptr)); | |
| 319 return; | |
| 320 } | 367 } |
| 321 | 368 |
| 322 audit_proof_queries_.emplace_back(new AuditProofQuery( | 369 AuditProofQuery* query = new AuditProofQuery( |
| 323 dns_client_.get(), domain_for_log, tree_size, net_log_)); | 370 dns_client_.get(), domain_for_log.as_string(), tree_size, net_log_); |
| 371 // Transfers ownership of |query| to |audit_proof_queries_|. | |
| 372 audit_proof_queries_.emplace_back(query); | |
| 324 | 373 |
| 325 AuditProofQuery::CompletionCallback internal_callback = | 374 AuditProofQuery::CompletionCallback internal_callback = |
| 326 base::Bind(&LogDnsClient::QueryAuditProofComplete, | 375 base::Bind(&LogDnsClient::QueryAuditProofComplete, |
| 327 weak_ptr_factory_.GetWeakPtr(), callback); | 376 weak_ptr_factory_.GetWeakPtr(), callback); |
| 328 | 377 |
| 329 audit_proof_queries_.back()->Start(leaf_hash, internal_callback); | 378 return query->Start(leaf_hash, internal_callback); |
| 330 } | 379 } |
| 331 | 380 |
| 332 void LogDnsClient::QueryAuditProofComplete(const AuditProofCallback& callback, | 381 void LogDnsClient::QueryAuditProofComplete(const AuditProofCallback& callback, |
| 333 int result, | 382 net::Error result, |
| 334 AuditProofQuery* query) { | 383 AuditProofQuery* query) { |
| 335 DCHECK(query); | 384 DCHECK(query); |
| 336 | 385 |
| 337 std::unique_ptr<net::ct::MerkleAuditProof> proof; | 386 std::unique_ptr<net::ct::MerkleAuditProof> proof; |
| 338 if (result == net::OK) { | 387 if (result == net::OK) { |
| 339 proof = query->TakeProof(); | 388 proof = query->TakeProof(); |
| 340 } | 389 } |
| 341 | 390 |
| 342 // Finished with the query - destroy it. Do this before invoking |callback| | 391 // Finished with the query - destroy it. Do this before invoking |callback| |
| 343 // in case it wants to perform another query and |audit_proof_queries_| is | 392 // in case it wants to perform another query and |audit_proof_queries_| is |
| 344 // already at its limit (as specified by |max_concurrent_queries_|. | 393 // already at its limit (as specified by |max_concurrent_queries_|). |
| 345 auto query_iterator = | 394 auto query_iterator = |
| 346 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), | 395 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), |
| 347 [query](const std::unique_ptr<AuditProofQuery>& p) { | 396 [query](const std::unique_ptr<AuditProofQuery>& p) { |
| 348 return p.get() == query; | 397 return p.get() == query; |
| 349 }); | 398 }); |
| 350 DCHECK(query_iterator != audit_proof_queries_.end()); | 399 DCHECK(query_iterator != audit_proof_queries_.end()); |
| 351 audit_proof_queries_.erase(query_iterator); | 400 audit_proof_queries_.erase(query_iterator); |
| 352 | 401 |
| 353 base::ThreadTaskRunnerHandle::Get()->PostTask( | 402 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 354 FROM_HERE, base::Bind(callback, result, base::Passed(&proof))); | 403 FROM_HERE, base::Bind(callback, result, base::Passed(&proof))); |
| 355 } | 404 } |
| 356 | 405 |
| 357 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { | 406 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { |
| 358 return max_concurrent_queries_ != 0 && | 407 return max_concurrent_queries_ != 0 && |
| 359 audit_proof_queries_.size() >= max_concurrent_queries_; | 408 audit_proof_queries_.size() >= max_concurrent_queries_; |
| 360 } | 409 } |
| 361 | 410 |
| 362 void LogDnsClient::UpdateDnsConfig() { | 411 void LogDnsClient::UpdateDnsConfig() { |
| 363 net::DnsConfig config; | 412 net::DnsConfig config; |
| 364 net::NetworkChangeNotifier::GetDnsConfig(&config); | 413 net::NetworkChangeNotifier::GetDnsConfig(&config); |
| 365 if (config.IsValid()) | 414 if (config.IsValid()) |
| 366 dns_client_->SetConfig(config); | 415 dns_client_->SetConfig(config); |
| 367 } | 416 } |
| 368 | 417 |
| 369 } // namespace certificate_transparency | 418 } // namespace certificate_transparency |
| OLD | NEW |