Index: net/quic/chromium/quic_stream_factory.cc |
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc |
index 09fb8e62bf91078fe9640b0b56a2b41e25de43c7..b7739f9d7ab9702fff97745a8646e69d1f7fa198 100644 |
--- a/net/quic/chromium/quic_stream_factory.cc |
+++ b/net/quic/chromium/quic_stream_factory.cc |
@@ -40,6 +40,7 @@ |
#include "net/quic/chromium/quic_chromium_connection_helper.h" |
#include "net/quic/chromium/quic_chromium_packet_reader.h" |
#include "net/quic/chromium/quic_chromium_packet_writer.h" |
+#include "net/quic/core/crypto/proof_verifier.h" |
#include "net/quic/core/crypto/properties_based_quic_server_info.h" |
#include "net/quic/core/crypto/quic_random.h" |
#include "net/quic/core/crypto/quic_server_info.h" |
@@ -167,6 +168,88 @@ QuicConfig InitializeQuicConfig(const QuicTagVector& connection_options, |
} // namespace |
+// Responsible for verifying the certificates saved in |
+// QuicCryptoClientConfig, and for notifying any associated requests when |
+// complete. Results from cert verification are ignored. |
+class QuicStreamFactory::CertVerifierJob { |
+ public: |
+ // ProofVerifierCallbackImpl is passed as the callback method to |
+ // VerifyCertChain. The ProofVerifier calls this class with the result of cert |
+ // verification when verification is performed asynchronously. |
+ class ProofVerifierCallbackImpl : public ProofVerifierCallback { |
+ public: |
+ explicit ProofVerifierCallbackImpl(CertVerifierJob* job) : job_(job) {} |
+ |
+ ~ProofVerifierCallbackImpl() override {} |
+ |
+ void Run(bool ok, |
+ const std::string& error_details, |
+ std::unique_ptr<ProofVerifyDetails>* details) override { |
+ if (job_ == nullptr) |
+ return; |
+ job_->verify_callback_ = nullptr; |
+ job_->OnComplete(); |
+ } |
+ |
+ void Cancel() { job_ = nullptr; } |
+ |
+ private: |
+ CertVerifierJob* job_; |
+ }; |
+ |
+ CertVerifierJob(const QuicServerId& server_id, |
+ int cert_verify_flags, |
+ const BoundNetLog& net_log) |
+ : server_id_(server_id), |
+ verify_callback_(nullptr), |
+ verify_context_(base::WrapUnique( |
+ new ProofVerifyContextChromium(cert_verify_flags, net_log))), |
+ net_log_(net_log), |
+ weak_factory_(this) {} |
+ |
+ ~CertVerifierJob() { |
+ if (verify_callback_) |
+ verify_callback_->Cancel(); |
+ } |
+ |
+ // Starts verification of certs cached in the |crypto_config|. |
+ QuicAsyncStatus Run(QuicCryptoClientConfig* crypto_config, |
+ const CompletionCallback& callback) { |
+ QuicCryptoClientConfig::CachedState* cached = |
+ crypto_config->LookupOrCreate(server_id_); |
+ ProofVerifierCallbackImpl* verify_callback = |
+ new ProofVerifierCallbackImpl(this); |
+ QuicAsyncStatus status = crypto_config->proof_verifier()->VerifyCertChain( |
+ server_id_.host(), cached->certs(), verify_context_.get(), |
+ &verify_error_details_, &verify_details_, |
+ std::unique_ptr<ProofVerifierCallback>(verify_callback)); |
+ if (status == QUIC_PENDING) { |
+ verify_callback_ = verify_callback; |
+ callback_ = callback; |
+ } |
+ return status; |
+ } |
+ |
+ void OnComplete() { |
+ if (!callback_.is_null()) |
+ callback_.Run(OK); |
+ } |
+ |
+ const QuicServerId& server_id() const { return server_id_; } |
+ |
+ private: |
+ QuicServerId server_id_; |
+ ProofVerifierCallbackImpl* verify_callback_; |
+ std::unique_ptr<ProofVerifyContext> verify_context_; |
+ std::unique_ptr<ProofVerifyDetails> verify_details_; |
+ std::string verify_error_details_; |
+ const BoundNetLog net_log_; |
+ CompletionCallback callback_; |
+ base::WeakPtrFactory<CertVerifierJob> weak_factory_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CertVerifierJob); |
+}; |
+ |
// Responsible for creating a new QUIC session to the specified server, and |
// for notifying any associated requests when complete. |
class QuicStreamFactory::Job { |
@@ -364,9 +447,8 @@ void QuicStreamFactory::Job::CancelWaitForDataReadyCallback() { |
int QuicStreamFactory::Job::DoResolveHost() { |
// Start loading the data now, and wait for it after we resolve the host. |
- if (server_info_) { |
+ if (server_info_) |
server_info_->Start(); |
- } |
io_state_ = STATE_RESOLVE_HOST_COMPLETE; |
dns_resolution_start_time_ = base::TimeTicks::Now(); |
@@ -388,9 +470,8 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) { |
// Inform the factory of this resolution, which will set up |
// a session alias, if possible. |
- if (factory_->OnResolution(key_, address_list_)) { |
+ if (factory_->OnResolution(key_, address_list_)) |
return OK; |
- } |
if (server_info_) |
io_state_ = STATE_LOAD_SERVER_INFO; |
@@ -469,14 +550,12 @@ int QuicStreamFactory::Job::DoConnect() { |
return rv; |
} |
- if (!session_->connection()->connected()) { |
+ if (!session_->connection()->connected()) |
return ERR_CONNECTION_CLOSED; |
- } |
session_->StartReading(); |
- if (!session_->connection()->connected()) { |
+ if (!session_->connection()->connected()) |
return ERR_QUIC_PROTOCOL_ERROR; |
- } |
bool require_confirmation = factory_->require_confirmation() || |
was_alternative_service_recently_broken_; |
@@ -485,8 +564,9 @@ int QuicStreamFactory::Job::DoConnect() { |
base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr())); |
if (!session_->connection()->connected() && |
- session_->error() == QUIC_PROOF_INVALID) |
+ session_->error() == QUIC_PROOF_INVALID) { |
return ERR_QUIC_HANDSHAKE_FAILED; |
+ } |
return rv; |
} |
@@ -503,9 +583,8 @@ int QuicStreamFactory::Job::DoResumeConnect() { |
int QuicStreamFactory::Job::DoConnectComplete(int rv) { |
if (session_ && session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { |
num_sent_client_hellos_ += session_->GetNumSentClientHellos(); |
- if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) { |
+ if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) |
return ERR_QUIC_HANDSHAKE_FAILED; |
- } |
// The handshake was rejected statelessly, so create another connection |
// to resume the handshake. |
io_state_ = STATE_CONNECT; |
@@ -634,6 +713,7 @@ QuicStreamFactory::QuicStreamFactory( |
bool migrate_sessions_early, |
bool allow_server_migration, |
bool force_hol_blocking, |
+ bool race_cert_verification, |
const QuicTagVector& connection_options, |
bool enable_token_binding) |
: require_confirmation_(true), |
@@ -690,6 +770,7 @@ QuicStreamFactory::QuicStreamFactory( |
migrate_sessions_on_network_change_), |
allow_server_migration_(allow_server_migration), |
force_hol_blocking_(force_hol_blocking), |
+ race_cert_verification_(race_cert_verification), |
port_seed_(random_generator_->RandUint64()), |
check_persisted_supports_quic_(true), |
has_initialized_data_(false), |
@@ -757,6 +838,8 @@ QuicStreamFactory::~QuicStreamFactory() { |
STLDeleteElements(&(active_jobs_[server_id])); |
active_jobs_.erase(server_id); |
} |
+ while (!active_cert_verifier_jobs_.empty()) |
+ active_cert_verifier_jobs_.erase(active_cert_verifier_jobs_.begin()); |
if (ssl_config_service_.get()) |
ssl_config_service_->RemoveObserver(this); |
if (migrate_sessions_on_network_change_) { |
@@ -888,11 +971,12 @@ int QuicStreamFactory::Create(const QuicServerId& server_id, |
// don't wait for Cache thread to load the data for that server. |
load_from_disk_cache = false; |
} |
- if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) { |
+ if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) |
quic_server_info = quic_server_info_factory_->GetForServer(server_id); |
- } |
} |
+ ignore_result(StartCertVerifyJob(server_id, cert_verify_flags, net_log)); |
+ |
QuicSessionKey key(destination, server_id); |
std::unique_ptr<Job> job( |
new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id), |
@@ -953,9 +1037,8 @@ bool QuicStreamFactory::OnResolution(const QuicSessionKey& key, |
const AddressList& address_list) { |
const QuicServerId& server_id(key.server_id()); |
DCHECK(!HasActiveSession(server_id)); |
- if (disable_connection_pooling_) { |
+ if (disable_connection_pooling_) |
return false; |
- } |
for (const IPEndPoint& address : address_list) { |
if (!ContainsKey(ip_aliases_, address)) |
continue; |
@@ -1025,6 +1108,10 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { |
job_requests_map_.erase(server_id); |
} |
+void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) { |
+ active_cert_verifier_jobs_.erase(job->server_id()); |
+} |
+ |
std::unique_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession( |
QuicChromiumClientSession* session) { |
return std::unique_ptr<QuicHttpStream>( |
@@ -1140,9 +1227,8 @@ void QuicStreamFactory::OnSessionGoingAway(QuicChromiumClientSession* session) { |
DCHECK_EQ(session, active_sessions_[server_id]); |
// Track sessions which have recently gone away so that we can disable |
// port suggestions. |
- if (session->goaway_received()) { |
+ if (session->goaway_received()) |
gone_away_aliases_.insert(*it); |
- } |
active_sessions_.erase(server_id); |
ProcessGoingAwaySession(session, server_id, true); |
@@ -1151,9 +1237,8 @@ void QuicStreamFactory::OnSessionGoingAway(QuicChromiumClientSession* session) { |
if (!aliases.empty()) { |
const IPEndPoint peer_address = session->connection()->peer_address(); |
ip_aliases_[peer_address].erase(session); |
- if (ip_aliases_[peer_address].empty()) { |
+ if (ip_aliases_[peer_address].empty()) |
ip_aliases_.erase(peer_address); |
- } |
} |
session_aliases_.erase(session); |
} |
@@ -1258,9 +1343,8 @@ void QuicStreamFactory::OnSessionConnectTimeout( |
QuicChromiumClientSession* session) { |
const AliasSet& aliases = session_aliases_[session]; |
- if (aliases.empty()) { |
+ if (aliases.empty()) |
return; |
- } |
for (const QuicSessionKey& key : aliases) { |
const QuicServerId& server_id = key.server_id(); |
@@ -1272,9 +1356,8 @@ void QuicStreamFactory::OnSessionConnectTimeout( |
const IPEndPoint peer_address = session->connection()->peer_address(); |
ip_aliases_[peer_address].erase(session); |
- if (ip_aliases_[peer_address].empty()) { |
+ if (ip_aliases_[peer_address].empty()) |
ip_aliases_.erase(peer_address); |
- } |
QuicSessionKey key = *aliases.begin(); |
session_aliases_.erase(session); |
Job* job = new Job(this, host_resolver_, session, key); |
@@ -1370,9 +1453,8 @@ NetworkHandle QuicStreamFactory::FindAlternateNetwork( |
NetworkChangeNotifier::NetworkList network_list; |
NetworkChangeNotifier::GetConnectedNetworks(&network_list); |
for (NetworkHandle new_network : network_list) { |
- if (new_network != old_network) { |
+ if (new_network != old_network) |
return new_network; |
- } |
} |
return NetworkChangeNotifier::kInvalidNetworkHandle; |
} |
@@ -1580,6 +1662,11 @@ bool QuicStreamFactory::HasActiveJob(const QuicServerId& server_id) const { |
return ContainsKey(active_jobs_, server_id); |
} |
+bool QuicStreamFactory::HasActiveCertVerifierJob( |
+ const QuicServerId& server_id) const { |
+ return ContainsKey(active_cert_verifier_jobs_, server_id); |
+} |
+ |
int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket, |
IPEndPoint addr, |
NetworkHandle network) { |
@@ -1668,15 +1755,13 @@ int QuicStreamFactory::CreateSession( |
// Passing in kInvalidNetworkHandle binds socket to default network. |
int rv = ConfigureSocket(socket.get(), addr, |
NetworkChangeNotifier::kInvalidNetworkHandle); |
- if (rv != OK) { |
+ if (rv != OK) |
return rv; |
- } |
- if (enable_port_selection) { |
+ if (enable_port_selection) |
DCHECK_LE(1u, port_suggester->call_count()); |
- } else { |
+ else |
DCHECK_EQ(0u, port_suggester->call_count()); |
- } |
if (!helper_.get()) { |
helper_.reset( |
@@ -1790,6 +1875,29 @@ bool QuicStreamFactory::CryptoConfigCacheIsEmpty( |
return cached->IsEmpty(); |
} |
+QuicAsyncStatus QuicStreamFactory::StartCertVerifyJob( |
+ const QuicServerId& server_id, |
+ int cert_verify_flags, |
+ const BoundNetLog& net_log) { |
+ if (!race_cert_verification_) |
+ return QUIC_FAILURE; |
+ QuicCryptoClientConfig::CachedState* cached = |
+ crypto_config_.LookupOrCreate(server_id); |
+ if (!cached || cached->certs().empty() || |
+ HasActiveCertVerifierJob(server_id)) { |
+ return QUIC_FAILURE; |
+ } |
+ std::unique_ptr<CertVerifierJob> cert_verifier_job( |
+ new CertVerifierJob(server_id, cert_verify_flags, net_log)); |
+ QuicAsyncStatus status = cert_verifier_job->Run( |
+ &crypto_config_, |
+ base::Bind(&QuicStreamFactory::OnCertVerifyJobComplete, |
+ base::Unretained(this), cert_verifier_job.get())); |
+ if (status == QUIC_PENDING) |
+ active_cert_verifier_jobs_[server_id] = std::move(cert_verifier_job); |
+ return status; |
+} |
+ |
void QuicStreamFactory::InitializeCachedStateInCryptoConfig( |
const QuicServerId& server_id, |
const std::unique_ptr<QuicServerInfo>& server_info, |