| // Copyright (c) 2021 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quic/core/crypto/quic_client_session_cache.h" | |
| #include "quic/core/quic_clock.h" | |
| namespace quic { | |
| namespace { | |
| const size_t kDefaultMaxEntries = 1024; | |
| // Returns false if the SSL |session| doesn't exist or it is expired at |now|. | |
| bool IsValid(SSL_SESSION* session, uint64_t now) { | |
| if (!session) return false; | |
| // now_u64 may be slightly behind because of differences in how | |
| // time is calculated at this layer versus BoringSSL. | |
| // Add a second of wiggle room to account for this. | |
| return !(now + 1 < SSL_SESSION_get_time(session) || | |
| now >= SSL_SESSION_get_time(session) + | |
| SSL_SESSION_get_timeout(session)); | |
| } | |
| bool DoApplicationStatesMatch(const ApplicationState* state, | |
| ApplicationState* other) { | |
| if ((state && !other) || (!state && other)) return false; | |
| if ((!state && !other) || *state == *other) return true; | |
| return false; | |
| } | |
| } // namespace | |
| QuicClientSessionCache::QuicClientSessionCache() | |
| : QuicClientSessionCache(kDefaultMaxEntries) {} | |
| QuicClientSessionCache::QuicClientSessionCache(size_t max_entries) | |
| : cache_(max_entries) {} | |
| QuicClientSessionCache::~QuicClientSessionCache() { Clear(); } | |
| void QuicClientSessionCache::Insert(const QuicServerId& server_id, | |
| bssl::UniquePtr<SSL_SESSION> session, | |
| const TransportParameters& params, | |
| const ApplicationState* application_state) { | |
| QUICHE_DCHECK(session) << "TLS session is not inserted into client cache."; | |
| auto iter = cache_.Lookup(server_id); | |
| if (iter == cache_.end()) { | |
| CreateAndInsertEntry(server_id, std::move(session), params, | |
| application_state); | |
| return; | |
| } | |
| QUICHE_DCHECK(iter->second->params); | |
| // The states are both the same, so only need to insert sessions. | |
| if (params == *iter->second->params && | |
| DoApplicationStatesMatch(application_state, | |
| iter->second->application_state.get())) { | |
| iter->second->PushSession(std::move(session)); | |
| return; | |
| } | |
| // Erase the existing entry because this Insert call must come from a | |
| // different QUIC session. | |
| cache_.Erase(iter); | |
| CreateAndInsertEntry(server_id, std::move(session), params, | |
| application_state); | |
| } | |
| std::unique_ptr<QuicResumptionState> QuicClientSessionCache::Lookup( | |
| const QuicServerId& server_id, QuicWallTime now, const SSL_CTX* /*ctx*/) { | |
| auto iter = cache_.Lookup(server_id); | |
| if (iter == cache_.end()) return nullptr; | |
| if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) { | |
| QUIC_DLOG(INFO) << "TLS Session expired for host:" << server_id.host(); | |
| cache_.Erase(iter); | |
| return nullptr; | |
| } | |
| auto state = std::make_unique<QuicResumptionState>(); | |
| state->tls_session = iter->second->PopSession(); | |
| if (iter->second->params != nullptr) { | |
| state->transport_params = | |
| std::make_unique<TransportParameters>(*iter->second->params); | |
| } | |
| if (iter->second->application_state != nullptr) { | |
| state->application_state = | |
| std::make_unique<ApplicationState>(*iter->second->application_state); | |
| } | |
| if (GetQuicReloadableFlag(quic_tls_use_token_in_session_cache) && | |
| !iter->second->token.empty()) { | |
| state->token = iter->second->token; | |
| // Clear token after use. | |
| iter->second->token.clear(); | |
| } | |
| return state; | |
| } | |
| void QuicClientSessionCache::ClearEarlyData(const QuicServerId& server_id) { | |
| auto iter = cache_.Lookup(server_id); | |
| if (iter == cache_.end()) return; | |
| for (auto& session : iter->second->sessions) { | |
| if (session) { | |
| QUIC_DLOG(INFO) << "Clear early data for for host: " << server_id.host(); | |
| session.reset(SSL_SESSION_copy_without_early_data(session.get())); | |
| } | |
| } | |
| } | |
| void QuicClientSessionCache::OnNewTokenReceived(const QuicServerId& server_id, | |
| absl::string_view token) { | |
| if (token.empty()) { | |
| return; | |
| } | |
| auto iter = cache_.Lookup(server_id); | |
| if (iter == cache_.end()) { | |
| return; | |
| } | |
| iter->second->token = std::string(token); | |
| } | |
| void QuicClientSessionCache::RemoveExpiredEntries(QuicWallTime now) { | |
| auto iter = cache_.begin(); | |
| while (iter != cache_.end()) { | |
| if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) { | |
| iter = cache_.Erase(iter); | |
| } else { | |
| ++iter; | |
| } | |
| } | |
| } | |
| void QuicClientSessionCache::Clear() { cache_.Clear(); } | |
| void QuicClientSessionCache::CreateAndInsertEntry( | |
| const QuicServerId& server_id, bssl::UniquePtr<SSL_SESSION> session, | |
| const TransportParameters& params, | |
| const ApplicationState* application_state) { | |
| auto entry = std::make_unique<Entry>(); | |
| entry->PushSession(std::move(session)); | |
| entry->params = std::make_unique<TransportParameters>(params); | |
| if (application_state) { | |
| entry->application_state = | |
| std::make_unique<ApplicationState>(*application_state); | |
| } | |
| cache_.Insert(server_id, std::move(entry)); | |
| } | |
| QuicClientSessionCache::Entry::Entry() = default; | |
| QuicClientSessionCache::Entry::Entry(Entry&&) = default; | |
| QuicClientSessionCache::Entry::~Entry() = default; | |
| void QuicClientSessionCache::Entry::PushSession( | |
| bssl::UniquePtr<SSL_SESSION> session) { | |
| if (sessions[0] != nullptr) { | |
| sessions[1] = std::move(sessions[0]); | |
| } | |
| sessions[0] = std::move(session); | |
| } | |
| bssl::UniquePtr<SSL_SESSION> QuicClientSessionCache::Entry::PopSession() { | |
| if (sessions[0] == nullptr) return nullptr; | |
| bssl::UniquePtr<SSL_SESSION> session = std::move(sessions[0]); | |
| sessions[0] = std::move(sessions[1]); | |
| sessions[1] = nullptr; | |
| return session; | |
| } | |
| SSL_SESSION* QuicClientSessionCache::Entry::PeekSession() { | |
| return sessions[0].get(); | |
| } | |
| } // namespace quic |