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 |