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" |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 | 74 |
| 75 for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) { | 75 for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) { |
| 76 proof->nodes.push_back(audit_path.substr(i, crypto::kSHA256Length)); | 76 proof->nodes.push_back(audit_path.substr(i, crypto::kSHA256Length)); |
| 77 } | 77 } |
| 78 | 78 |
| 79 return true; | 79 return true; |
| 80 } | 80 } |
| 81 | 81 |
| 82 } // namespace | 82 } // namespace |
| 83 | 83 |
| 84 struct LogDnsClient::Query | |
| 85 { | |
| 86 std::unique_ptr<net::DnsTransaction> transaction; | |
| 87 AuditProofCallback callback; | |
| 88 }; | |
| 89 | |
| 84 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, | 90 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, |
| 85 const net::BoundNetLog& net_log, | 91 const net::BoundNetLog& net_log, |
| 86 size_t max_concurrent_queries) | 92 size_t max_concurrent_queries) |
| 87 : dns_client_(std::move(dns_client)), | 93 : dns_client_(std::move(dns_client)), |
| 88 net_log_(net_log), | 94 net_log_(net_log), |
| 89 max_concurrent_queries_(max_concurrent_queries), | 95 max_concurrent_queries_(max_concurrent_queries), |
| 90 weak_ptr_factory_(this) { | 96 weak_ptr_factory_(this) { |
| 91 CHECK(dns_client_); | 97 CHECK(dns_client_); |
| 92 net::NetworkChangeNotifier::AddDNSObserver(this); | 98 net::NetworkChangeNotifier::AddDNSObserver(this); |
| 93 UpdateDnsConfig(); | 99 UpdateDnsConfig(); |
| 94 } | 100 } |
| 95 | 101 |
| 96 LogDnsClient::~LogDnsClient() { | 102 LogDnsClient::~LogDnsClient() { |
| 97 net::NetworkChangeNotifier::RemoveDNSObserver(this); | 103 net::NetworkChangeNotifier::RemoveDNSObserver(this); |
| 98 } | 104 } |
| 99 | 105 |
| 100 void LogDnsClient::OnDNSChanged() { | 106 void LogDnsClient::OnDNSChanged() { |
| 101 UpdateDnsConfig(); | 107 UpdateDnsConfig(); |
| 102 } | 108 } |
| 103 | 109 |
| 104 void LogDnsClient::OnInitialDNSConfigRead() { | 110 void LogDnsClient::OnInitialDNSConfigRead() { |
| 105 UpdateDnsConfig(); | 111 UpdateDnsConfig(); |
| 106 } | 112 } |
| 107 | 113 |
| 108 void LogDnsClient::QueryLeafIndex(base::StringPiece domain_for_log, | |
| 109 base::StringPiece leaf_hash, | |
| 110 const LeafIndexCallback& callback) { | |
| 111 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { | |
| 112 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 113 FROM_HERE, base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, 0)); | |
| 114 return; | |
| 115 } | |
| 116 | |
| 117 if (HasMaxConcurrentQueriesInProgress()) { | |
| 118 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 119 FROM_HERE, | |
| 120 base::Bind(callback, net::Error::ERR_TEMPORARILY_THROTTLED, 0)); | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 std::string encoded_leaf_hash = | |
| 125 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING); | |
| 126 DCHECK_EQ(encoded_leaf_hash.size(), 52u); | |
| 127 | |
| 128 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | |
| 129 if (factory == nullptr) { | |
| 130 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 131 FROM_HERE, | |
| 132 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, 0)); | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 std::string qname = base::StringPrintf( | |
| 137 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log.data()); | |
| 138 | |
| 139 net::DnsTransactionFactory::CallbackType transaction_callback = base::Bind( | |
| 140 &LogDnsClient::QueryLeafIndexComplete, weak_ptr_factory_.GetWeakPtr()); | |
| 141 | |
| 142 std::unique_ptr<net::DnsTransaction> dns_transaction = | |
| 143 factory->CreateTransaction(qname, net::dns_protocol::kTypeTXT, | |
| 144 transaction_callback, net_log_); | |
| 145 | |
| 146 dns_transaction->Start(); | |
| 147 leaf_index_queries_.push_back({std::move(dns_transaction), callback}); | |
| 148 } | |
| 149 | |
| 150 // The performance of this could be improved by sending all of the expected | 114 // The performance of this could be improved by sending all of the expected |
| 151 // queries up front. Each response can contain a maximum of 7 audit path nodes, | 115 // queries up front. Each response can contain a maximum of 7 audit path nodes, |
| 152 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6, | 116 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6, |
| 153 // 7-13 and 14-19) immediately. Currently, it sends only the first and then, | 117 // 7-13 and 14-19) immediately. Currently, it sends only the first and then, |
| 154 // based on the number of nodes received, sends the next query. The complexity | 118 // based on the number of nodes received, sends the next query. The complexity |
| 155 // of the code would increase though, as it would need to detect gaps in the | 119 // of the code would increase though, as it would need to detect gaps in the |
| 156 // audit proof caused by the server not responding with the anticipated number | 120 // audit proof caused by the server not responding with the anticipated number |
| 157 // of nodes. Ownership of the proof would need to change, as it would be shared | 121 // of nodes. Ownership of the proof would need to change, as it would be shared |
| 158 // between simultaneous DNS transactions. Throttling of queries would also need | 122 // between simultaneous DNS transactions. Throttling of queries would also need |
| 159 // to take into account this increase in parallelism. | 123 // to take into account this increase in parallelism. |
| 160 void LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log, | 124 void LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log, |
| 161 uint64_t leaf_index, | 125 base::StringPiece leaf_hash, |
| 162 uint64_t tree_size, | 126 uint64_t tree_size, |
| 163 const AuditProofCallback& callback) { | 127 const AuditProofCallback& callback) { |
| 164 if (domain_for_log.empty() || leaf_index >= tree_size) { | 128 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) { |
| 165 base::ThreadTaskRunnerHandle::Get()->PostTask( | 129 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 166 FROM_HERE, | 130 FROM_HERE, |
| 167 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr)); | 131 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr)); |
| 168 return; | 132 return; |
| 169 } | 133 } |
| 170 | 134 |
| 171 if (HasMaxConcurrentQueriesInProgress()) { | 135 if (HasMaxConcurrentQueriesInProgress()) { |
| 172 base::ThreadTaskRunnerHandle::Get()->PostTask( | 136 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 173 FROM_HERE, | 137 FROM_HERE, |
| 174 base::Bind(callback, net::Error::ERR_TEMPORARILY_THROTTLED, nullptr)); | 138 base::Bind(callback, net::Error::ERR_TEMPORARILY_THROTTLED, nullptr)); |
| 175 return; | 139 return; |
| 176 } | 140 } |
| 177 | 141 |
| 178 std::unique_ptr<net::ct::MerkleAuditProof> proof( | 142 QueryLeafIndex(domain_for_log, leaf_hash, tree_size, callback); |
|
Eran Messeri
2016/09/23 12:59:31
Nit: domain_for_log and tree_size seem to always b
Rob Percival
2016/09/27 17:44:17
Done.
| |
| 179 new net::ct::MerkleAuditProof); | |
| 180 proof->leaf_index = leaf_index; | |
| 181 // TODO(robpercival): Once a "tree_size" field is added to MerkleAuditProof, | |
| 182 // pass |tree_size| to QueryAuditProofNodes using that. | |
| 183 | |
| 184 // Query for the first batch of audit proof nodes (i.e. starting from 0). | |
| 185 QueryAuditProofNodes(std::move(proof), domain_for_log, tree_size, 0, | |
| 186 callback); | |
| 187 } | 143 } |
| 188 | 144 |
| 189 void LogDnsClient::QueryLeafIndexComplete(net::DnsTransaction* transaction, | 145 void LogDnsClient::QueryLeafIndex(base::StringPiece domain_for_log, |
| 146 base::StringPiece leaf_hash, | |
| 147 uint64_t tree_size, | |
| 148 const AuditProofCallback& callback) { | |
| 149 std::string encoded_leaf_hash = | |
| 150 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING); | |
| 151 DCHECK_EQ(encoded_leaf_hash.size(), 52u); | |
| 152 | |
| 153 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); | |
| 154 if (factory == nullptr) { | |
| 155 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 156 FROM_HERE, | |
| 157 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, nullptr)); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 std::string qname = base::StringPrintf( | |
| 162 "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log.data()); | |
| 163 | |
| 164 net::DnsTransactionFactory::CallbackType transaction_callback = | |
| 165 base::Bind(&LogDnsClient::QueryLeafIndexComplete, | |
| 166 weak_ptr_factory_.GetWeakPtr(), domain_for_log, tree_size); | |
|
Eran Messeri
2016/09/23 12:59:31
Here it seems like the tree_size and domain_for_lo
Rob Percival
2016/09/27 17:44:17
Done.
| |
| 167 | |
| 168 std::unique_ptr<net::DnsTransaction> dns_transaction = | |
| 169 factory->CreateTransaction(qname, net::dns_protocol::kTypeTXT, | |
| 170 transaction_callback, net_log_); | |
| 171 | |
| 172 dns_transaction->Start(); | |
| 173 audit_proof_queries_.push_back({std::move(dns_transaction), callback}); | |
| 174 } | |
| 175 | |
| 176 void LogDnsClient::QueryLeafIndexComplete(base::StringPiece domain_for_log, | |
| 177 uint64_t tree_size, | |
| 178 net::DnsTransaction* transaction, | |
| 190 int net_error, | 179 int net_error, |
| 191 const net::DnsResponse* response) { | 180 const net::DnsResponse* response) { |
| 192 auto query_iterator = | 181 auto query_iterator = |
| 193 std::find_if(leaf_index_queries_.begin(), leaf_index_queries_.end(), | 182 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), |
| 194 [transaction](const Query<LeafIndexCallback>& query) { | 183 [transaction](const Query& query) { |
| 195 return query.transaction.get() == transaction; | 184 return query.transaction.get() == transaction; |
| 196 }); | 185 }); |
| 197 if (query_iterator == leaf_index_queries_.end()) { | 186 if (query_iterator == audit_proof_queries_.end()) { |
| 198 NOTREACHED(); | 187 NOTREACHED(); |
| 199 return; | 188 return; |
| 200 } | 189 } |
| 201 const Query<LeafIndexCallback> query = std::move(*query_iterator); | 190 const Query query = std::move(*query_iterator); |
| 202 leaf_index_queries_.erase(query_iterator); | 191 audit_proof_queries_.erase(query_iterator); |
| 203 | 192 |
| 204 // If we've received no response but no net::error either (shouldn't happen), | 193 // If we've received no response but no net::error either (shouldn't happen), |
| 205 // report the response as invalid. | 194 // report the response as invalid. |
| 206 if (response == nullptr && net_error == net::OK) { | 195 if (response == nullptr && net_error == net::OK) { |
| 207 net_error = net::ERR_INVALID_RESPONSE; | 196 net_error = net::ERR_INVALID_RESPONSE; |
| 208 } | 197 } |
| 209 | 198 |
| 210 if (net_error != net::OK) { | 199 if (net_error != net::OK) { |
| 211 base::ThreadTaskRunnerHandle::Get()->PostTask( | 200 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 212 FROM_HERE, base::Bind(query.callback, net_error, 0)); | 201 FROM_HERE, base::Bind(query.callback, net_error, nullptr)); |
| 213 return; | 202 return; |
| 214 } | 203 } |
| 215 | 204 |
| 216 uint64_t leaf_index; | 205 std::unique_ptr<net::ct::MerkleAuditProof> proof( |
| 217 if (!ParseLeafIndex(*response, &leaf_index)) { | 206 new net::ct::MerkleAuditProof); |
| 207 | |
| 208 if (!ParseLeafIndex(*response, &proof->leaf_index)) { | |
| 218 base::ThreadTaskRunnerHandle::Get()->PostTask( | 209 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 219 FROM_HERE, | 210 FROM_HERE, |
| 220 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, 0)); | 211 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, nullptr)); |
| 221 return; | 212 return; |
| 222 } | 213 } |
| 223 | 214 |
| 224 base::ThreadTaskRunnerHandle::Get()->PostTask( | 215 // Reject leaf index if it is out-of-range. |
| 225 FROM_HERE, base::Bind(query.callback, net::OK, leaf_index)); | 216 // This indicates either: |
| 217 // a) the wrong tree_size was provided. | |
| 218 // b) the wrong leaf hash was provided. | |
| 219 // c) there is a bug server-side. | |
| 220 // The first two are more likely, so return ERR_INVALID_ARGUMENT. | |
| 221 if (proof->leaf_index >= tree_size) { | |
| 222 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 223 FROM_HERE, | |
| 224 base::Bind(query.callback, net::ERR_INVALID_ARGUMENT, nullptr)); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 // TODO(robpercival): Once a "tree_size" field is added to MerkleAuditProof, | |
| 229 // pass |tree_size| to QueryAuditProofNodes using that. | |
| 230 | |
| 231 // Query for the first batch of audit proof nodes (i.e. starting from 0). | |
| 232 QueryAuditProofNodes(std::move(proof), domain_for_log, tree_size, 0, | |
|
Eran Messeri
2016/09/23 12:59:32
Document the 0:
tree_size, 0 /* start node index *
Rob Percival
2016/09/27 17:44:17
Done.
| |
| 233 query.callback); | |
| 226 } | 234 } |
| 227 | 235 |
| 228 void LogDnsClient::QueryAuditProofNodes( | 236 void LogDnsClient::QueryAuditProofNodes( |
| 229 std::unique_ptr<net::ct::MerkleAuditProof> proof, | 237 std::unique_ptr<net::ct::MerkleAuditProof> proof, |
| 230 base::StringPiece domain_for_log, | 238 base::StringPiece domain_for_log, |
| 231 uint64_t tree_size, | 239 uint64_t tree_size, |
| 232 uint64_t node_index, | 240 uint64_t node_index, |
| 233 const AuditProofCallback& callback) { | 241 const AuditProofCallback& callback) { |
| 234 // Preconditions that should be guaranteed internally by this class. | 242 // Preconditions that should be guaranteed internally by this class. |
| 235 DCHECK(proof); | 243 DCHECK(proof); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 uint64_t tree_size, | 276 uint64_t tree_size, |
| 269 net::DnsTransaction* transaction, | 277 net::DnsTransaction* transaction, |
| 270 int net_error, | 278 int net_error, |
| 271 const net::DnsResponse* response) { | 279 const net::DnsResponse* response) { |
| 272 // Preconditions that should be guaranteed internally by this class. | 280 // Preconditions that should be guaranteed internally by this class. |
| 273 DCHECK(proof); | 281 DCHECK(proof); |
| 274 DCHECK(!domain_for_log.empty()); | 282 DCHECK(!domain_for_log.empty()); |
| 275 | 283 |
| 276 auto query_iterator = | 284 auto query_iterator = |
| 277 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), | 285 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), |
| 278 [transaction](const Query<AuditProofCallback>& query) { | 286 [transaction](const Query& query) { |
| 279 return query.transaction.get() == transaction; | 287 return query.transaction.get() == transaction; |
| 280 }); | 288 }); |
| 281 | 289 |
| 282 if (query_iterator == audit_proof_queries_.end()) { | 290 if (query_iterator == audit_proof_queries_.end()) { |
| 283 NOTREACHED(); | 291 NOTREACHED(); |
| 284 return; | 292 return; |
| 285 } | 293 } |
| 286 const Query<AuditProofCallback> query = std::move(*query_iterator); | 294 const Query query = std::move(*query_iterator); |
| 287 audit_proof_queries_.erase(query_iterator); | 295 audit_proof_queries_.erase(query_iterator); |
| 288 | 296 |
| 289 // If we've received no response but no net::error either (shouldn't happen), | 297 // If we've received no response but no net::error either (shouldn't happen), |
| 290 // report the response as invalid. | 298 // report the response as invalid. |
| 291 if (response == nullptr && net_error == net::OK) { | 299 if (response == nullptr && net_error == net::OK) { |
| 292 net_error = net::ERR_INVALID_RESPONSE; | 300 net_error = net::ERR_INVALID_RESPONSE; |
| 293 } | 301 } |
| 294 | 302 |
| 295 if (net_error != net::OK) { | 303 if (net_error != net::OK) { |
| 296 base::ThreadTaskRunnerHandle::Get()->PostTask( | 304 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 315 audit_path_nodes_received, query.callback); | 323 audit_path_nodes_received, query.callback); |
| 316 return; | 324 return; |
| 317 } | 325 } |
| 318 | 326 |
| 319 base::ThreadTaskRunnerHandle::Get()->PostTask( | 327 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 320 FROM_HERE, | 328 FROM_HERE, |
| 321 base::Bind(query.callback, net::OK, base::Passed(std::move(proof)))); | 329 base::Bind(query.callback, net::OK, base::Passed(std::move(proof)))); |
| 322 } | 330 } |
| 323 | 331 |
| 324 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { | 332 bool LogDnsClient::HasMaxConcurrentQueriesInProgress() const { |
| 325 const size_t queries_in_progress = | |
| 326 leaf_index_queries_.size() + audit_proof_queries_.size(); | |
| 327 | |
| 328 return max_concurrent_queries_ != 0 && | 333 return max_concurrent_queries_ != 0 && |
| 329 queries_in_progress >= max_concurrent_queries_; | 334 audit_proof_queries_.size() >= max_concurrent_queries_; |
| 330 } | 335 } |
| 331 | 336 |
| 332 void LogDnsClient::UpdateDnsConfig() { | 337 void LogDnsClient::UpdateDnsConfig() { |
| 333 net::DnsConfig config; | 338 net::DnsConfig config; |
| 334 net::NetworkChangeNotifier::GetDnsConfig(&config); | 339 net::NetworkChangeNotifier::GetDnsConfig(&config); |
| 335 if (config.IsValid()) | 340 if (config.IsValid()) |
| 336 dns_client_->SetConfig(config); | 341 dns_client_->SetConfig(config); |
| 337 } | 342 } |
| 338 | 343 |
| 339 } // namespace certificate_transparency | 344 } // namespace certificate_transparency |
| OLD | NEW |