diff options
author | Cronet Mainline Eng <cronet-mainline-eng+copybara@google.com> | 2023-04-18 07:37:34 -0800 |
---|---|---|
committer | Patrick Rohr <prohr@google.com> | 2023-04-18 08:40:26 -0700 |
commit | eddec18c18cdbcbdbbe9bf3c0fa24cb7f8d768ae (patch) | |
tree | 2983326030c4b680835550dca47bc960e77c492f /net | |
parent | 41cb724250484f326b0bbd5f8b955eb37b3b83c6 (diff) | |
download | cronet-eddec18c18cdbcbdbbe9bf3c0fa24cb7f8d768ae.tar.gz |
Import Cronet version 114.0.5715.0
Project import generated by Copybara.
FolderOrigin-RevId: /tmp/copybara-origin/src
Test: none
Change-Id: I15627f4badf0f6173d24f6c555169cc815a29fdd
Diffstat (limited to 'net')
95 files changed, 2380 insertions, 907 deletions
diff --git a/net/BUILD.gn b/net/BUILD.gn index e2953c9b4..ade5bfdff 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn @@ -660,6 +660,7 @@ component("net") { "http/http_status_code.cc", "http/http_status_code.h", "http/http_status_code_list.h", + "http/http_stream.cc", "http/http_stream.h", "http/http_stream_factory.cc", "http/http_stream_factory.h", @@ -1218,6 +1219,13 @@ component("net") { ] } + if (is_linux) { + sources += [ + "base/address_map_cache_linux.cc", + "base/address_map_cache_linux.h", + ] + } + if (is_chromeos) { deps += [ "//third_party/xdg_shared_mime_info" ] } @@ -3060,7 +3068,6 @@ test("net_unittests") { "websockets/websocket_deflate_predictor_impl_test.cc", "websockets/websocket_deflate_stream_test.cc", "websockets/websocket_deflater_test.cc", - "websockets/websocket_end_to_end_test.cc", "websockets/websocket_errors_test.cc", "websockets/websocket_extension_parser_test.cc", "websockets/websocket_extension_test.cc", @@ -3076,6 +3083,14 @@ test("net_unittests") { "websockets/websocket_test_util.cc", "websockets/websocket_test_util.h", ] + + if (!is_ios) { + # TODO(crbug.com/1281277): iOS does not have support for the spawned test + # server, which is used by this test. The long term plan is to add + # websocket support to the embedded test server and when that happens, + # this test could be enabled. + sources += [ "websockets/websocket_end_to_end_test.cc" ] + } } if (!disable_file_support) { diff --git a/net/android/dummy_spnego_authenticator.h b/net/android/dummy_spnego_authenticator.h index f2f22f667..6a3981db9 100644 --- a/net/android/dummy_spnego_authenticator.h +++ b/net/android/dummy_spnego_authenticator.h @@ -12,6 +12,7 @@ #include <string> #include "base/android/scoped_java_ref.h" +#include "base/memory/raw_ptr_exclusion.h" // Provides an interface for controlling the DummySpnegoAuthenticator service. // This includes a basic stub of the Mock GSSAPI library, so that OS independent @@ -26,7 +27,9 @@ namespace net { typedef struct gss_OID_desc_struct { uint32_t length; - void* elements; + // This field is not a raw_ptr<> because it was filtered by the rewriter for: + // #global-scope + RAW_PTR_EXCLUSION void* elements; } gss_OID_desc, *gss_OID; extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC; diff --git a/net/base/address_map_cache_linux.cc b/net/base/address_map_cache_linux.cc new file mode 100644 index 000000000..13aefdc22 --- /dev/null +++ b/net/base/address_map_cache_linux.cc @@ -0,0 +1,49 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/address_map_cache_linux.h" + +#include <linux/rtnetlink.h> + +#include "base/synchronization/lock.h" + +namespace net { + +AddressMapCacheLinux::AddressMapCacheLinux() = default; +AddressMapCacheLinux::~AddressMapCacheLinux() = default; + +AddressMapOwnerLinux::AddressMap AddressMapCacheLinux::GetAddressMap() const { + base::AutoLock autolock(lock_); + return cached_address_map_; +} + +std::unordered_set<int> AddressMapCacheLinux::GetOnlineLinks() const { + base::AutoLock autolock(lock_); + return cached_online_links_; +} + +void AddressMapCacheLinux::ApplyDiffs(const AddressMapDiff& addr_diff, + const OnlineLinksDiff& links_diff) { + base::AutoLock autolock(lock_); + for (const auto& [address, msg_opt] : addr_diff) { + if (msg_opt.has_value()) { + cached_address_map_[address] = msg_opt.value(); + } else { + DCHECK(cached_address_map_.count(address)); + cached_address_map_.erase(address); + } + } + + for (const auto& [if_index, is_now_online] : links_diff) { + if (is_now_online) { + DCHECK_EQ(cached_online_links_.count(if_index), 0u); + cached_online_links_.insert(if_index); + } else { + DCHECK_EQ(cached_online_links_.count(if_index), 1u); + cached_online_links_.erase(if_index); + } + } +} + +} // namespace net diff --git a/net/base/address_map_cache_linux.h b/net/base/address_map_cache_linux.h new file mode 100644 index 000000000..8c45c6c87 --- /dev/null +++ b/net/base/address_map_cache_linux.h @@ -0,0 +1,51 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_ +#define NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_ + +#include <map> +#include <string> +#include <unordered_set> + +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "net/base/address_map_linux.h" +#include "net/base/net_export.h" + +namespace net { + +// This caches AddressMap and the set of online links (see AddressMapOwnerLinux) +// so AddressTrackerLinux doesn't need to always be running in every process. +// This class is thread-safe. +class NET_EXPORT AddressMapCacheLinux : public AddressMapOwnerLinux { + public: + AddressMapCacheLinux(); + + AddressMapCacheLinux(const AddressMapCacheLinux&) = delete; + AddressMapCacheLinux& operator=(const AddressMapCacheLinux&) = delete; + + ~AddressMapCacheLinux() override; + + // AddressMapOwnerLinux implementation: + AddressMap GetAddressMap() const override; + std::unordered_set<int> GetOnlineLinks() const override; + + // Takes the diffs and applies them (atomically) to `cached_address_map_` and + // `cached_online_links_`. + // Once this method returns, calls on other threads to GetAddressMap() and + // GetOnlineLinks() that happen-after this call should return the updated + // data. + void ApplyDiffs(const AddressMapDiff& addr_diff, + const OnlineLinksDiff& links_diff); + + private: + mutable base::Lock lock_; + AddressMap cached_address_map_ GUARDED_BY(lock_); + std::unordered_set<int> cached_online_links_ GUARDED_BY(lock_); +}; + +} // namespace net + +#endif // NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_ diff --git a/net/base/address_map_linux.h b/net/base/address_map_linux.h index 49bc35bb9..6f10b112b 100644 --- a/net/base/address_map_linux.h +++ b/net/base/address_map_linux.h @@ -29,6 +29,20 @@ class NET_EXPORT AddressMapOwnerLinux { // with (e.g. interface index). using AddressMap = std::map<IPAddress, struct ifaddrmsg>; + // Represents a diff between one AddressMap and a new one. IPAddresses that + // map to absl::nullopt have been deleted from the map, and IPAddresses that + // map to non-nullopt have been added or updated. + using AddressMapDiff = + base::flat_map<IPAddress, absl::optional<struct ifaddrmsg>>; + // Represents a diff between one set of online links and new one. Interface + // indices that map to true are newly online and indices that map to false are + // newly offline. + using OnlineLinksDiff = base::flat_map<int, bool>; + // A callback for diffs, to be used by AddressTrackerLinux. + using DiffCallback = + base::RepeatingCallback<void(const AddressMapDiff& addr_diff, + const OnlineLinksDiff&)>; + AddressMapOwnerLinux() = default; AddressMapOwnerLinux(const AddressMapOwnerLinux&) = delete; diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc index d90e022b2..431820f20 100644 --- a/net/base/address_tracker_linux.cc +++ b/net/base/address_tracker_linux.cc @@ -15,6 +15,7 @@ #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "base/sequence_checker.h" #include "base/task/current_thread.h" #include "base/threading/scoped_blocking_call.h" #include "base/threading/thread_restrictions.h" @@ -172,11 +173,13 @@ AddressTrackerLinux::AddressTrackerLinux( tracking_(true) { DCHECK(!address_callback.is_null()); DCHECK(!link_callback.is_null()); + DETACH_FROM_SEQUENCE(sequence_checker_); } AddressTrackerLinux::~AddressTrackerLinux() = default; void AddressTrackerLinux::Init() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); #if BUILDFLAG(IS_ANDROID) // RTM_GETLINK stopped working in Android 11 (see // https://developer.android.com/preview/privacy/mac-address), @@ -241,7 +244,8 @@ void AddressTrackerLinux::Init() { bool address_changed; bool link_changed; bool tunnel_changed; - ReadMessages(&address_changed, &link_changed, &tunnel_changed); + ReadMessages(&address_changed, &link_changed, &tunnel_changed, nullptr, + nullptr); // Request dump of link state request.header.nlmsg_type = RTM_GETLINK; @@ -256,7 +260,8 @@ void AddressTrackerLinux::Init() { } // Consume pending message to populate links_online_, but don't notify. - ReadMessages(&address_changed, &link_changed, &tunnel_changed); + ReadMessages(&address_changed, &link_changed, &tunnel_changed, nullptr, + nullptr); { AddressTrackerAutoLock lock(*this, connection_type_lock_); connection_type_initialized_ = true; @@ -272,11 +277,13 @@ void AddressTrackerLinux::Init() { } bool AddressTrackerLinux::DidTrackingInitSucceedForTesting() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(tracking_); return watcher_ != nullptr; } void AddressTrackerLinux::AbortAndForceOnline() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); watcher_.reset(); netlink_fd_.reset(); AddressTrackerAutoLock lock(*this, connection_type_lock_); @@ -295,7 +302,28 @@ std::unordered_set<int> AddressTrackerLinux::GetOnlineLinks() const { return online_links_; } +void AddressTrackerLinux::SetDiffCallback(DiffCallback diff_callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + diff_callback_ = std::move(diff_callback); + + // Send the initial configuration to the diff callback. + AddressMap address_map = GetAddressMap(); + AddressMapDiff address_map_diff; + for (const std::pair<const IPAddress, struct ifaddrmsg>& it : address_map) { + address_map_diff[it.first] = it.second; + } + + std::unordered_set<int> online_links = GetOnlineLinks(); + OnlineLinksDiff online_links_diff; + for (int online_link : online_links) { + online_links_diff[online_link] = true; + } + + diff_callback_.Run(address_map_diff, online_links_diff); +} + bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (ignored_interfaces_.empty()) return false; @@ -320,7 +348,10 @@ AddressTrackerLinux::GetCurrentConnectionType() { void AddressTrackerLinux::ReadMessages(bool* address_changed, bool* link_changed, - bool* tunnel_changed) { + bool* tunnel_changed, + AddressMapDiff* address_map_diff, + OnlineLinksDiff* online_links_diff) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); *address_changed = false; *link_changed = false; *tunnel_changed = false; @@ -349,7 +380,8 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed, PLOG(ERROR) << "Failed to recv from netlink socket"; return; } - HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); + HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed, + address_map_diff, online_links_diff); } } if (*link_changed || *address_changed) @@ -360,7 +392,10 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, int length, bool* address_changed, bool* link_changed, - bool* tunnel_changed) { + bool* tunnel_changed, + AddressMapDiff* address_map_diff, + OnlineLinksDiff* online_links_diff) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(buffer); // Note that NLMSG_NEXT decrements |length| to reflect the number of bytes // remaining in |buffer|. @@ -411,6 +446,9 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, it->second = msg_copy; *address_changed = true; } + if (*address_changed && address_map_diff) { + (*address_map_diff)[address] = msg_copy; + } } } break; case RTM_DELADDR: { @@ -423,8 +461,12 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, break; if (GetAddress(header, length, &address, nullptr)) { AddressTrackerAutoLock lock(*this, address_map_lock_); - if (address_map_.erase(address)) + if (address_map_.erase(address)) { *address_changed = true; + if (address_map_diff) { + (*address_map_diff)[address] = absl::nullopt; + } + } } } break; case RTM_NEWLINK: { @@ -443,6 +485,9 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, AddressTrackerAutoLock lock(*this, online_links_lock_); if (online_links_.insert(msg->ifi_index).second) { *link_changed = true; + if (online_links_diff) { + (*online_links_diff)[msg->ifi_index] = true; + } if (IsTunnelInterface(msg->ifi_index)) *tunnel_changed = true; } @@ -450,6 +495,9 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, AddressTrackerAutoLock lock(*this, online_links_lock_); if (online_links_.erase(msg->ifi_index)) { *link_changed = true; + if (online_links_diff) { + (*online_links_diff)[msg->ifi_index] = false; + } if (IsTunnelInterface(msg->ifi_index)) *tunnel_changed = true; } @@ -465,6 +513,9 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, AddressTrackerAutoLock lock(*this, online_links_lock_); if (online_links_.erase(msg->ifi_index)) { *link_changed = true; + if (online_links_diff) { + (*online_links_diff)[msg->ifi_index] = false; + } if (IsTunnelInterface(msg->ifi_index)) *tunnel_changed = true; } @@ -476,10 +527,20 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, } void AddressTrackerLinux::OnFileCanReadWithoutBlocking() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bool address_changed; bool link_changed; bool tunnel_changed; - ReadMessages(&address_changed, &link_changed, &tunnel_changed); + if (diff_callback_) { + AddressMapDiff address_map_diff; + OnlineLinksDiff online_links_diff; + ReadMessages(&address_changed, &link_changed, &tunnel_changed, + &address_map_diff, &online_links_diff); + diff_callback_.Run(address_map_diff, online_links_diff); + } else { + ReadMessages(&address_changed, &link_changed, &tunnel_changed, nullptr, + nullptr); + } if (address_changed) address_callback_.Run(); if (link_changed) @@ -500,6 +561,7 @@ bool AddressTrackerLinux::IsTunnelInterfaceName(const char* name) { } void AddressTrackerLinux::UpdateCurrentConnectionType() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); AddressTrackerLinux::AddressMap address_map = GetAddressMap(); std::unordered_set<int> online_links = GetOnlineLinks(); @@ -539,7 +601,7 @@ AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( if (tracker_->tracking_) { lock_->Acquire(); } else { - DCHECK(tracker_->thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_); } } @@ -547,6 +609,8 @@ AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { if (tracker_->tracking_) { lock_->AssertAcquired(); lock_->Release(); + } else { + DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_); } } diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h index 3ac4f30a5..dea5f2057 100644 --- a/net/base/address_tracker_linux.h +++ b/net/base/address_tracker_linux.h @@ -25,9 +25,10 @@ #include "base/functional/callback.h" #include "base/gtest_prod_util.h" #include "base/memory/raw_ref.h" +#include "base/sequence_checker.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" +#include "base/thread_annotations.h" #include "net/base/address_map_linux.h" #include "net/base/ip_address.h" #include "net/base/net_export.h" @@ -79,6 +80,17 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux { // Returns set of interface indices for online interfaces. std::unordered_set<int> GetOnlineLinks() const override; + // Whenever the AddressMap or the set of online links (returned by the above + // two methods) changes, this callback is called on AddressTrackerLinux's + // sequence. On the first call, |diff_callback| is called synchronously with + // the current AddressMap and set of online links. + // + // This is only available in tracking mode, and must be called on + // AddressTrackerLinux's sequence. Note that other threads may see updated + // AddressMaps by calling GetAddressMap() before |diff_callback| is ever + // called. + void SetDiffCallback(DiffCallback diff_callback); + // Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType(). // Safe to call from any thread, but will block until Init() has completed. NetworkChangeNotifier::ConnectionType GetCurrentConnectionType(); @@ -103,13 +115,13 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux { // In tracking mode, holds |lock| while alive. In non-tracking mode, // enforces single-threaded access. - class AddressTrackerAutoLock { + class SCOPED_LOCKABLE AddressTrackerAutoLock { public: - AddressTrackerAutoLock(const AddressTrackerLinux& tracker, - base::Lock& lock); + AddressTrackerAutoLock(const AddressTrackerLinux& tracker, base::Lock& lock) + EXCLUSIVE_LOCK_FUNCTION(lock); AddressTrackerAutoLock(const AddressTrackerAutoLock&) = delete; AddressTrackerAutoLock& operator=(const AddressTrackerAutoLock&) = delete; - ~AddressTrackerAutoLock(); + ~AddressTrackerAutoLock() UNLOCK_FUNCTION(); private: const raw_ref<const AddressTrackerLinux> tracker_; @@ -125,19 +137,35 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux { // sets |*link_changed| to indicate if |online_links_| changed and sets // |*tunnel_changed| to indicate if |online_links_| changed with regards to a // tunnel interface while reading messages from |netlink_fd_|. + // + // If |address_map_| has changed and |address_map_diff| is not nullptr, + // |*address_map_diff| is populated with the changes to the AddressMap. + // Similarly, if |online_links_| has changed and |online_links_diff| is not + // nullptr, |*online_links_diff| is populated with the changes to the set of + // online links. void ReadMessages(bool* address_changed, bool* link_changed, - bool* tunnel_changed); + bool* tunnel_changed, + AddressMapDiff* address_map_diff, + OnlineLinksDiff* online_links_diff); // Sets |*address_changed| to true if |address_map_| changed, sets // |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed| // to true if |online_links_| changed with regards to a tunnel interface while // reading the message from |buffer|. + // + // If |address_map_| has changed and |address_map_diff| is not nullptr, + // |*address_map_diff| is populated with the changes to the AddressMap. + // Similarly, if |online_links_| has changed and |online_links_diff| is not + // nullptr, |*online_links_diff| is populated with the changes to the set of + // online links. void HandleMessage(const char* buffer, int length, bool* address_changed, bool* link_changed, - bool* tunnel_changed); + bool* tunnel_changed, + AddressMapDiff* address_map_diff, + OnlineLinksDiff* online_links_diff); // Call when some part of initialization failed; forces online and unblocks. void AbortAndForceOnline(); @@ -167,34 +195,38 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux { // overridden by tests. GetInterfaceNameFunction get_interface_name_; - base::RepeatingClosure address_callback_; - base::RepeatingClosure link_callback_; - base::RepeatingClosure tunnel_callback_; + DiffCallback diff_callback_ GUARDED_BY_CONTEXT(sequence_checker_); + base::RepeatingClosure address_callback_ + GUARDED_BY_CONTEXT(sequence_checker_); + base::RepeatingClosure link_callback_ GUARDED_BY_CONTEXT(sequence_checker_); + base::RepeatingClosure tunnel_callback_ GUARDED_BY_CONTEXT(sequence_checker_); // Note that |watcher_| must be inactive when |netlink_fd_| is closed. - base::ScopedFD netlink_fd_; - std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_; + base::ScopedFD netlink_fd_ GUARDED_BY_CONTEXT(sequence_checker_); + std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_ + GUARDED_BY_CONTEXT(sequence_checker_); mutable base::Lock address_map_lock_; - AddressMap address_map_; + AddressMap address_map_ GUARDED_BY(address_map_lock_); // Set of interface indices for links that are currently online. mutable base::Lock online_links_lock_; - std::unordered_set<int> online_links_; + std::unordered_set<int> online_links_ GUARDED_BY(online_links_lock_); // Set of interface names that should be ignored. const std::unordered_set<std::string> ignored_interfaces_; base::Lock connection_type_lock_; - bool connection_type_initialized_ = false; + bool connection_type_initialized_ GUARDED_BY(connection_type_lock_) = false; base::ConditionVariable connection_type_initialized_cv_; - NetworkChangeNotifier::ConnectionType current_connection_type_ = - NetworkChangeNotifier::CONNECTION_NONE; - bool tracking_; - int threads_waiting_for_connection_type_initialization_ = 0; + NetworkChangeNotifier::ConnectionType current_connection_type_ GUARDED_BY( + connection_type_lock_) = NetworkChangeNotifier::CONNECTION_NONE; + int threads_waiting_for_connection_type_initialization_ + GUARDED_BY(connection_type_lock_) = 0; + + const bool tracking_; - // Used to verify single-threaded access in non-tracking mode. - base::ThreadChecker thread_checker_; + SEQUENCE_CHECKER(sequence_checker_); }; } // namespace net::internal diff --git a/net/base/address_tracker_linux_fuzzer.cc b/net/base/address_tracker_linux_fuzzer.cc index 7a0fd530a..0013d88e9 100644 --- a/net/base/address_tracker_linux_fuzzer.cc +++ b/net/base/address_tracker_linux_fuzzer.cc @@ -18,7 +18,7 @@ class AddressTrackerLinuxTest { base::DoNothing(), ignored_interfaces); bool address_changed, link_changed, tunnel_changed; tracker.HandleMessage(buffer, length, &address_changed, &link_changed, - &tunnel_changed); + &tunnel_changed, nullptr, nullptr); } }; diff --git a/net/base/address_tracker_linux_unittest.cc b/net/base/address_tracker_linux_unittest.cc index 00a0bc7be..2a83474bb 100644 --- a/net/base/address_tracker_linux_unittest.cc +++ b/net/base/address_tracker_linux_unittest.cc @@ -5,6 +5,7 @@ #include "net/base/address_tracker_linux.h" #include <linux/if.h> +#include <linux/rtnetlink.h> #include <sched.h> #include <memory> @@ -18,12 +19,14 @@ #include "base/memory/raw_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/test/bind.h" #include "base/test/multiprocess_test.h" #include "base/test/spin_wait.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/simple_thread.h" #include "build/build_config.h" +#include "net/base/address_map_cache_linux.h" #include "net/base/ip_address.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" @@ -36,6 +39,10 @@ #define IFA_F_HOMEADDRESS 0x10 #endif +bool operator==(const struct ifaddrmsg& lhs, const struct ifaddrmsg& rhs) { + return memcmp(&lhs, &rhs, sizeof(struct ifaddrmsg)) == 0; +} + namespace net::internal { namespace { @@ -84,8 +91,12 @@ class AddressTrackerLinuxTest : public testing::Test { bool address_changed = false; bool link_changed = false; bool tunnel_changed = false; - tracker_->HandleMessage(&writable_buf[0], buf.size(), - &address_changed, &link_changed, &tunnel_changed); + AddressMapOwnerLinux::AddressMapDiff address_map_diff_; + AddressMapOwnerLinux::OnlineLinksDiff online_links_diff_; + tracker_->HandleMessage(&writable_buf[0], buf.size(), &address_changed, + &link_changed, &tunnel_changed, &address_map_diff_, + &online_links_diff_); + UpdateCache(address_map_diff_, online_links_diff_); EXPECT_FALSE(link_changed); return address_changed; } @@ -95,8 +106,12 @@ class AddressTrackerLinuxTest : public testing::Test { bool address_changed = false; bool link_changed = false; bool tunnel_changed = false; - tracker_->HandleMessage(&writable_buf[0], buf.size(), - &address_changed, &link_changed, &tunnel_changed); + AddressMapOwnerLinux::AddressMapDiff address_map_diff_; + AddressMapOwnerLinux::OnlineLinksDiff online_links_diff_; + tracker_->HandleMessage(&writable_buf[0], buf.size(), &address_changed, + &link_changed, &tunnel_changed, &address_map_diff_, + &online_links_diff_); + UpdateCache(address_map_diff_, online_links_diff_); EXPECT_FALSE(address_changed); return link_changed; } @@ -106,8 +121,12 @@ class AddressTrackerLinuxTest : public testing::Test { bool address_changed = false; bool link_changed = false; bool tunnel_changed = false; - tracker_->HandleMessage(&writable_buf[0], buf.size(), - &address_changed, &link_changed, &tunnel_changed); + AddressMapOwnerLinux::AddressMapDiff address_map_diff_; + AddressMapOwnerLinux::OnlineLinksDiff online_links_diff_; + tracker_->HandleMessage(&writable_buf[0], buf.size(), &address_changed, + &link_changed, &tunnel_changed, &address_map_diff_, + &online_links_diff_); + UpdateCache(address_map_diff_, online_links_diff_); EXPECT_FALSE(address_changed); return tunnel_changed; } @@ -131,6 +150,23 @@ class AddressTrackerLinuxTest : public testing::Test { std::unordered_set<std::string> ignored_interfaces_; std::unique_ptr<AddressTrackerLinux> tracker_; AddressTrackerLinux::GetInterfaceNameFunction original_get_interface_name_; + + private: + // Checks that applying the generated diff to `address_map_cache_` results in + // the same AddressMap and set of online links that `tracker_` maintains. + void UpdateCache( + const AddressMapOwnerLinux::AddressMapDiff& address_map_diff, + const AddressMapOwnerLinux::OnlineLinksDiff& online_links_diff) { +#if BUILDFLAG(IS_LINUX) + address_map_cache_.ApplyDiffs(address_map_diff, online_links_diff); + EXPECT_EQ(address_map_cache_.GetAddressMap(), tracker_->GetAddressMap()); + EXPECT_EQ(address_map_cache_.GetOnlineLinks(), tracker_->GetOnlineLinks()); +#endif // BUILDFLAG(IS_LINUX) + } + +#if BUILDFLAG(IS_LINUX) + AddressMapCacheLinux address_map_cache_; +#endif }; namespace { diff --git a/net/base/features.cc b/net/base/features.cc index f4b8dbc6a..5d5178bc0 100644 --- a/net/base/features.cc +++ b/net/base/features.cc @@ -134,15 +134,6 @@ BASE_FEATURE(kPostQuantumKyber, "PostQuantumKyber", base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kPostQuantumCECPQ2, - "PostQuantumCECPQ2", - base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kPostQuantumCECPQ2SomeDomains, - "PostQuantumCECPQ2SomeDomains", - base::FEATURE_DISABLED_BY_DEFAULT); -const base::FeatureParam<std::string> - kPostQuantumCECPQ2Prefix(&kPostQuantumCECPQ2SomeDomains, "prefix", "a"); - BASE_FEATURE(kNetUnusedIdleSocketTimeout, "NetUnusedIdleSocketTimeout", base::FEATURE_DISABLED_BY_DEFAULT); diff --git a/net/base/features.h b/net/base/features.h index 0ba56e82b..bf578bebb 100644 --- a/net/base/features.h +++ b/net/base/features.h @@ -175,17 +175,6 @@ NET_EXPORT BASE_DECLARE_FEATURE(kPermuteTLSExtensions); // Enables Kyber-based post-quantum key-agreements in TLS 1.3 connections. NET_EXPORT BASE_DECLARE_FEATURE(kPostQuantumKyber); -// Enables CECPQ2, a post-quantum key-agreement, in TLS 1.3 connections. -// Ineffective if kPostQuantumKyber is enabled. -NET_EXPORT BASE_DECLARE_FEATURE(kPostQuantumCECPQ2); - -// Enables CECPQ2, a post-quantum key-agreement, in TLS 1.3 connections for a -// subset of domains. (This is intended as Finch kill-switch. For testing -// compatibility with large ClientHello messages, use |kPostQuantumCECPQ2|.) -NET_EXPORT BASE_DECLARE_FEATURE(kPostQuantumCECPQ2SomeDomains); -NET_EXPORT extern const base::FeatureParam<std::string> - kPostQuantumCECPQ2Prefix; - // Changes the timeout after which unused sockets idle sockets are cleaned up. NET_EXPORT BASE_DECLARE_FEATURE(kNetUnusedIdleSocketTimeout); diff --git a/net/base/ip_address.cc b/net/base/ip_address.cc index 45a915c19..eb0a7094c 100644 --- a/net/base/ip_address.cc +++ b/net/base/ip_address.cc @@ -419,17 +419,7 @@ std::string IPAddressToPackedString(const IPAddress& address) { } IPAddress ConvertIPv4ToIPv4MappedIPv6(const IPAddress& address) { - // TODO(https://crbug.com/1414007): Remove crash key and use DCHECK() when - // the cause is identified. - if (!address.IsIPv4()) { - static base::debug::CrashKeyString* crash_key = - base::debug::AllocateCrashKeyString("ipaddress", - base::debug::CrashKeySize::Size64); - base::debug::ScopedCrashKeyString addr(crash_key, address.ToString()); - bool is_valid = address.IsValid(); - base::debug::Alias(&is_valid); - LOG(FATAL) << "expected an IPv4 address but got " << address.ToString(); - } + CHECK(address.IsIPv4()); // IPv4-mapped addresses are formed by: // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. base::StackVector<uint8_t, 16> bytes; @@ -454,10 +444,10 @@ bool IPAddressMatchesPrefix(const IPAddress& ip_address, size_t prefix_length_in_bits) { // Both the input IP address and the prefix IP address should be either IPv4 // or IPv6. - DCHECK(ip_address.IsValid()); - DCHECK(ip_prefix.IsValid()); + CHECK(ip_address.IsValid()); + CHECK(ip_prefix.IsValid()); - DCHECK_LE(prefix_length_in_bits, ip_prefix.size() * 8); + CHECK_LE(prefix_length_in_bits, ip_prefix.size() * 8); // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to // IPv6 addresses in order to do the comparison. diff --git a/net/base/parse_number.h b/net/base/parse_number.h index f4827f7a7..f923b18a7 100644 --- a/net/base/parse_number.h +++ b/net/base/parse_number.h @@ -5,6 +5,8 @@ #ifndef NET_BASE_PARSE_NUMBER_H_ #define NET_BASE_PARSE_NUMBER_H_ +#include <cstdint> + #include "base/strings/string_piece.h" #include "net/base/net_export.h" diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc index f87fc159d..cfb73ea63 100644 --- a/net/cert/cert_verifier.cc +++ b/net/cert/cert_verifier.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/strings/string_util.h" +#include "base/types/optional_util.h" #include "build/build_config.h" #include "net/base/features.h" #include "net/cert/caching_cert_verifier.h" @@ -27,29 +28,25 @@ class DefaultCertVerifyProcFactory : public net::CertVerifyProcFactory { public: scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc( scoped_refptr<net::CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const net::ChromeRootStoreData* root_store_data) override { - scoped_refptr<net::CertVerifyProc> verify_proc; + const CertVerifyProcFactory::ImplParams& impl_params) override { #if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) - if (!verify_proc && - base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) { - verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( - std::move(cert_net_fetcher), std::move(crl_set), root_store_data); + if (impl_params.use_chrome_root_store) { + return CertVerifyProc::CreateBuiltinWithChromeRootStore( + std::move(cert_net_fetcher), impl_params.crl_set, + base::OptionalToPtr(impl_params.root_store_data)); } #endif - if (!verify_proc) { #if BUILDFLAG(CHROME_ROOT_STORE_ONLY) - verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( - std::move(cert_net_fetcher), std::move(crl_set), root_store_data); + return CertVerifyProc::CreateBuiltinWithChromeRootStore( + std::move(cert_net_fetcher), impl_params.crl_set, + base::OptionalToPtr(impl_params.root_store_data)); #elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - verify_proc = CertVerifyProc::CreateBuiltinVerifyProc( - std::move(cert_net_fetcher), std::move(crl_set)); + return CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher), + impl_params.crl_set); #else - verify_proc = CertVerifyProc::CreateSystemVerifyProc( - std::move(cert_net_fetcher), std::move(crl_set)); + return CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher), + impl_params.crl_set); #endif - } - return verify_proc; } private: @@ -119,8 +116,7 @@ CertVerifier::CreateDefaultWithoutCaching( scoped_refptr<CertNetFetcher> cert_net_fetcher) { auto proc_factory = base::MakeRefCounted<DefaultCertVerifyProcFactory>(); return std::make_unique<MultiThreadedCertVerifier>( - proc_factory->CreateCertVerifyProc(std::move(cert_net_fetcher), - net::CRLSet::BuiltinCRLSet(), nullptr), + proc_factory->CreateCertVerifyProc(std::move(cert_net_fetcher), {}), proc_factory); } diff --git a/net/cert/cert_verifier.h b/net/cert/cert_verifier.h index 19b2520c1..026ffc8ec 100644 --- a/net/cert/cert_verifier.h +++ b/net/cert/cert_verifier.h @@ -16,15 +16,14 @@ #include "net/base/hash_value.h" #include "net/base/net_export.h" #include "net/cert/cert_net_fetcher.h" +#include "net/cert/cert_verify_proc.h" #include "net/cert/x509_certificate.h" namespace net { class CertVerifyResult; class CertVerifierWithUpdatableProc; -class CRLSet; class NetLogWithSource; -class ChromeRootStoreData; // CertVerifier represents a service for verifying certificates. // @@ -239,11 +238,10 @@ NET_EXPORT bool operator!=(const CertVerifier::Config& lhs, // A CertVerifier that can update its CertVerifyProc while it is running. class NET_EXPORT CertVerifierWithUpdatableProc : public CertVerifier { public: - // Update the CertVerifyProc with new CRLSet and ChromeRootStoreData. + // Update the CertVerifyProc with a new set of parameters. virtual void UpdateVerifyProcData( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) = 0; + const net::CertVerifyProcFactory::ImplParams& impl_params) = 0; }; } // namespace net diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc index a9b1d6de4..8f4ed6e1b 100644 --- a/net/cert/cert_verify_proc.cc +++ b/net/cert/cert_verify_proc.cc @@ -892,4 +892,21 @@ bool CertVerifyProc::HasTooLongValidity(const X509Certificate& cert) { return false; } +CertVerifyProcFactory::ImplParams::ImplParams() { + crl_set = net::CRLSet::BuiltinCRLSet(); +#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) + use_chrome_root_store = + base::FeatureList::IsEnabled(net::features::kChromeRootStoreUsed); +#endif +} + +CertVerifyProcFactory::ImplParams::~ImplParams() = default; + +CertVerifyProcFactory::ImplParams::ImplParams(const ImplParams&) = default; +CertVerifyProcFactory::ImplParams& CertVerifyProcFactory::ImplParams::operator=( + const ImplParams& other) = default; +CertVerifyProcFactory::ImplParams::ImplParams(ImplParams&&) = default; +CertVerifyProcFactory::ImplParams& CertVerifyProcFactory::ImplParams::operator=( + ImplParams&& other) = default; + } // namespace net diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h index f1d698f82..31401d45f 100644 --- a/net/cert/cert_verify_proc.h +++ b/net/cert/cert_verify_proc.h @@ -17,6 +17,10 @@ #include "net/base/net_export.h" #include "net/net_buildflags.h" +#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) +#include "net/cert/internal/trust_store_chrome.h" +#endif + namespace net { class CertNetFetcher; @@ -24,7 +28,6 @@ class CertVerifyResult; class CRLSet; class NetLogWithSource; class X509Certificate; -class ChromeRootStoreData; typedef std::vector<scoped_refptr<X509Certificate>> CertificateList; // Class to perform certificate path building and verification for various @@ -36,17 +39,14 @@ class NET_EXPORT CertVerifyProc enum VerifyFlags { // If set, enables online revocation checking via CRLs and OCSP for the // certificate chain. + // Note: has no effect if VERIFY_DISABLE_NETWORK_FETCHES is set. VERIFY_REV_CHECKING_ENABLED = 1 << 0, // If set, this is equivalent to VERIFY_REV_CHECKING_ENABLED, in that it // enables online revocation checking via CRLs or OCSP, but only // for certificates issued by non-public trust anchors. Failure to check // revocation is treated as a hard failure. - // Note: If VERIFY_CERT_IO_ENABLE is not also supplied, certificates - // that chain to local trust anchors will likely fail - for example, due to - // lacking fresh cached revocation issue (Windows) or because OCSP stapling - // can only provide information for the leaf, and not for any - // intermediates. + // Note: has no effect if VERIFY_DISABLE_NETWORK_FETCHES is set. VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS = 1 << 1, // If set, certificates with SHA-1 signatures will be allowed, but only if @@ -56,6 +56,12 @@ class NET_EXPORT CertVerifyProc // If set, disables the policy enforcement described at // https://security.googleblog.com/2017/09/chromes-plan-to-distrust-symantec.html VERIFY_DISABLE_SYMANTEC_ENFORCEMENT = 1 << 3, + + // Disable network fetches during verification. This will override + // VERIFY_REV_CHECKING_ENABLED and + // VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS if they are also specified. + // TODO(https://crbug.com/1432793): This should also disable AIA fetching. + VERIFY_DISABLE_NETWORK_FETCHES = 1 << 4, }; // These values are persisted to logs. Entries should not be renumbered and @@ -213,12 +219,35 @@ class NET_EXPORT CertVerifyProc class NET_EXPORT CertVerifyProcFactory : public base::RefCountedThreadSafe<CertVerifyProcFactory> { public: + // The set of factory parameters that are variable over time, but are + // expected to be consistent between multiple verifiers that are created. For + // example, CertNetFetcher is not in this struct as it is expected that + // different verifiers will have different net fetchers. (There is no + // technical restriction against creating different verifiers with different + // ImplParams, structuring the parameters this way just makes some APIs more + // convenient for the common case.) + struct NET_EXPORT ImplParams { + ImplParams(); + ~ImplParams(); + ImplParams(const ImplParams&); + ImplParams& operator=(const ImplParams& other); + ImplParams(ImplParams&&); + ImplParams& operator=(ImplParams&& other); + + scoped_refptr<CRLSet> crl_set; +#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) + absl::optional<net::ChromeRootStoreData> root_store_data; +#endif +#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) + bool use_chrome_root_store; +#endif + }; + // Create a new CertVerifyProc that uses the passed in CRLSet and // ChromeRootStoreData. virtual scoped_refptr<CertVerifyProc> CreateCertVerifyProc( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) = 0; + const ImplParams& impl_params) = 0; protected: virtual ~CertVerifyProcFactory() = default; diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc index 907f295e7..dd9d25b89 100644 --- a/net/cert/cert_verify_proc_builtin.cc +++ b/net/cert/cert_verify_proc_builtin.cc @@ -314,6 +314,18 @@ class PathBuilderDelegateImpl : public SimplePathBuilderDelegate { // Selects a revocation policy based on the CertVerifier flags and the given // certificate chain. RevocationPolicy ChooseRevocationPolicy(const ParsedCertificateList& certs) { + if (flags_ & CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES) { + // In theory when network fetches are disabled but revocation is enabled + // we could continue with networking_allowed=false (and + // VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS would also have to change + // allow_missing_info and allow_unable_to_check to true). + // That theoretically could allow still consulting any cached CRLs/etc. + // However in the way things are currently implemented in the builtin + // verifier there really is no point to bothering, just disable + // revocation checking if network fetches are disabled. + return NoRevocationChecking(); + } + // Use hard-fail revocation checking for local trust anchors, if requested // by the load flag and the chain uses a non-public root. if ((flags_ & CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) && diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc index 784a7d0f8..d93de8aa7 100644 --- a/net/cert/cert_verify_proc_unittest.cc +++ b/net/cert/cert_verify_proc_unittest.cc @@ -3299,6 +3299,36 @@ TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationHardFailNoCrls) { EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailNoCrlsDisableNetworkFetches) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + // Create certs which have no AIA or CRL distribution points. + const char kHostname[] = "www.example.com"; + auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3(); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain(); + ASSERT_TRUE(chain.get()); + + // Verify with flags for both hard-fail revocation checking for local anchors + // and disabling network fetches. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS | + CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CertificateList(), &verify_result); + + // Should succeed, VERIFY_DISABLE_NETWORK_FETCHES takes priority. + EXPECT_THAT(error, IsOk()); + EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + // CRL hard fail test where both leaf and intermediate are covered by valid // CRLs which have empty (non-present) revokedCertificates list. Verification // should succeed. @@ -3656,6 +3686,42 @@ TEST_P(CertVerifyProcInternalWithNetFetchingTest, } TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailLeafRevokedByCrlDisableNetworkFetches) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3(); + + // Root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Leaf is revoked by intermediate issued CRL. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain(); + ASSERT_TRUE(chain.get()); + + // Verify with flags for both soft-fail revocation checking and disabling + // network fetches. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED | + CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CertificateList(), &verify_result); + + // Should succeed, VERIFY_DISABLE_NETWORK_FETCHES takes priority. + EXPECT_THAT(error, IsOk()); + EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationSoftFailIntermediateRevokedByCrl) { if (!SupportsSoftFailRevChecking()) { LOG(INFO) << "Skipping test as verifier doesn't support " diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc index 13cd5f040..0ac8dfc5f 100644 --- a/net/cert/multi_threaded_cert_verifier.cc +++ b/net/cert/multi_threaded_cert_verifier.cc @@ -149,8 +149,7 @@ void MultiThreadedCertVerifier::InternalRequest::Start( int flags = GetFlagsForConfig(config); if (params.flags() & CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES) { - flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; - flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + flags |= CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES; } base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, @@ -236,11 +235,10 @@ int MultiThreadedCertVerifier::Verify(const RequestParams& params, void MultiThreadedCertVerifier::UpdateVerifyProcData( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) { + const net::CertVerifyProcFactory::ImplParams& impl_params) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); verify_proc_ = verify_proc_factory_->CreateCertVerifyProc( - std::move(cert_net_fetcher), std::move(crl_set), root_store_data); + std::move(cert_net_fetcher), impl_params); NotifyCertVerifierChanged(); } diff --git a/net/cert/multi_threaded_cert_verifier.h b/net/cert/multi_threaded_cert_verifier.h index d5a01a1cd..368368cfc 100644 --- a/net/cert/multi_threaded_cert_verifier.h +++ b/net/cert/multi_threaded_cert_verifier.h @@ -28,7 +28,6 @@ namespace net { class CertVerifyProc; class CertNetFetcher; class CertVerifyProcFactory; -class ChromeRootStoreData; // MultiThreadedCertVerifier is a CertVerifier implementation that runs // synchronous CertVerifier implementations on worker threads. @@ -58,8 +57,7 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier void RemoveObserver(Observer* observer) override; void UpdateVerifyProcData( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) override; + const net::CertVerifyProcFactory::ImplParams& impl_params) override; private: class InternalRequest; diff --git a/net/cert/multi_threaded_cert_verifier_unittest.cc b/net/cert/multi_threaded_cert_verifier_unittest.cc index ce6577fb4..05fcc9fd9 100644 --- a/net/cert/multi_threaded_cert_verifier_unittest.cc +++ b/net/cert/multi_threaded_cert_verifier_unittest.cc @@ -83,8 +83,7 @@ class SwapWithNewProcFactory : public CertVerifyProcFactory { scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) override { + const ImplParams& impl_params) override { return mock_verify_proc_; } @@ -299,6 +298,37 @@ TEST_F(MultiThreadedCertVerifierTest, ConvertsConfigToFlags) { } } +// Tests propagation of CertVerifier flags into CertVerifyProc flags +TEST_F(MultiThreadedCertVerifierTest, ConvertsFlagsToFlags) { + scoped_refptr<X509Certificate> test_cert( + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); + ASSERT_TRUE(test_cert); + + EXPECT_CALL( + *mock_verify_proc_, + VerifyInternal(_, _, _, _, CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES, + _, _, _)) + .WillRepeatedly( + DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED))); + + CertVerifyResult verify_result; + TestCompletionCallback callback; + std::unique_ptr<CertVerifier::Request> request; + int error = verifier_->Verify( + CertVerifier::RequestParams(test_cert, "www.example.com", + CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()), + &verify_result, callback.callback(), &request, NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request); + error = callback.WaitForResult(); + EXPECT_TRUE(IsCertificateError(error)); + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + + testing::Mock::VerifyAndClearExpectations(mock_verify_proc_.get()); +} + // Tests swapping in new Chrome Root Store Data. TEST_F(MultiThreadedCertVerifierTest, VerifyProcChangeChromeRootStore) { CertVerifierObserverCounter observer_counter(verifier_.get()); @@ -313,7 +343,7 @@ TEST_F(MultiThreadedCertVerifierTest, VerifyProcChangeChromeRootStore) { EXPECT_CALL(*mock_new_verify_proc_, VerifyInternal(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED))); - verifier_->UpdateVerifyProcData(nullptr, nullptr, nullptr); + verifier_->UpdateVerifyProcData(nullptr, {}); EXPECT_EQ(observer_counter.change_count(), 1u); @@ -353,7 +383,7 @@ TEST_F(MultiThreadedCertVerifierTest, VerifyProcChangeRequest) { &verify_result, callback.callback(), &request, NetLogWithSource()); ASSERT_THAT(error, IsError(ERR_IO_PENDING)); EXPECT_TRUE(request); - verifier_->UpdateVerifyProcData(nullptr, nullptr, nullptr); + verifier_->UpdateVerifyProcData(nullptr, {}); error = callback.WaitForResult(); EXPECT_TRUE(IsCertificateError(error)); EXPECT_THAT(error, IsError(ERR_CERT_COMMON_NAME_INVALID)); diff --git a/net/cert/pki/string_util.h b/net/cert/pki/string_util.h index 1687b0886..b18f716f4 100644 --- a/net/cert/pki/string_util.h +++ b/net/cert/pki/string_util.h @@ -7,6 +7,8 @@ #include "net/base/net_export.h" +#include <stdint.h> + #include <string_view> #include <vector> diff --git a/net/cert/trial_comparison_cert_verifier.cc b/net/cert/trial_comparison_cert_verifier.cc index 2e7070c34..94d80b2b1 100644 --- a/net/cert/trial_comparison_cert_verifier.cc +++ b/net/cert/trial_comparison_cert_verifier.cc @@ -468,21 +468,29 @@ void TrialComparisonCertVerifier::Job::Request::OnJobAborted() { } TrialComparisonCertVerifier::TrialComparisonCertVerifier( - scoped_refptr<CertVerifyProc> primary_verify_proc, - scoped_refptr<CertVerifyProcFactory> primary_verify_proc_factory, - scoped_refptr<CertVerifyProc> trial_verify_proc, - scoped_refptr<CertVerifyProcFactory> trial_verify_proc_factory, + scoped_refptr<CertVerifyProcFactory> verify_proc_factory, + scoped_refptr<CertNetFetcher> cert_net_fetcher, + const CertVerifyProcFactory::ImplParams& impl_params, ReportCallback report_callback) - : report_callback_(std::move(report_callback)), - primary_verifier_(std::make_unique<MultiThreadedCertVerifier>( - primary_verify_proc, - primary_verify_proc_factory)), - primary_reverifier_(std::make_unique<MultiThreadedCertVerifier>( - primary_verify_proc, - primary_verify_proc_factory)), - trial_verifier_(std::make_unique<MultiThreadedCertVerifier>( - trial_verify_proc, - trial_verify_proc_factory)) { + : report_callback_(std::move(report_callback)) { + auto [primary_impl_params, trial_impl_params] = + ProcessImplParams(impl_params); + + primary_verifier_ = std::make_unique<MultiThreadedCertVerifier>( + verify_proc_factory->CreateCertVerifyProc(cert_net_fetcher, + primary_impl_params), + verify_proc_factory); + + primary_reverifier_ = std::make_unique<MultiThreadedCertVerifier>( + verify_proc_factory->CreateCertVerifyProc(cert_net_fetcher, + primary_impl_params), + verify_proc_factory); + + trial_verifier_ = std::make_unique<MultiThreadedCertVerifier>( + verify_proc_factory->CreateCertVerifyProc(cert_net_fetcher, + trial_impl_params), + verify_proc_factory); + primary_verifier_->AddObserver(this); primary_reverifier_->AddObserver(this); trial_verifier_->AddObserver(this); @@ -501,9 +509,18 @@ int TrialComparisonCertVerifier::Verify(const RequestParams& params, const NetLogWithSource& net_log) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!trial_allowed()) { - return primary_verifier_->Verify(params, verify_result, std::move(callback), + // Technically, the trial could still be active when + // actual_use_chrome_root_store_=true, just by reversing everything. (Doing + // the trial verifier first and returning that result, then doing the primary + // verifier and comparing.) Probably not worth the trouble though. + if (!trial_allowed() || actual_use_chrome_root_store_) { + if (actual_use_chrome_root_store_) { + return trial_verifier_->Verify(params, verify_result, std::move(callback), out_req, net_log); + } else { + return primary_verifier_->Verify(params, verify_result, + std::move(callback), out_req, net_log); + } } std::unique_ptr<Job> job = @@ -527,32 +544,53 @@ void TrialComparisonCertVerifier::SetConfig(const Config& config) { void TrialComparisonCertVerifier::AddObserver( CertVerifier::Observer* observer) { - // Let primary_verifier_ handle the observer lists rather than creating - // another one in TrialComparisonCertVerifier. From the perspective of the - // caller, the primary_verifier is the only one that it would care about - // changes to. + // Delegate the notifications to the wrapped verifiers. We add the observer + // to both `primary_verifier_` and `trial_verifier_` since either one might + // be the one that matters depending on the configuration at the time. This + // does mean the caller will get double notified, but shouldn't really matter + // that much. It would be possible to avoid this by making a notification + // proxy that only forwards notifications from the verifier that is currently + // the "main" one, but it's probably not worth the trouble. + // + // Also this assumes that there is never a case where the + // TrialComparisonCertVerifier itself would change something without the + // wrapped verifiers also generating a notification. primary_verifier_->AddObserver(observer); + trial_verifier_->AddObserver(observer); } void TrialComparisonCertVerifier::RemoveObserver( CertVerifier::Observer* observer) { + trial_verifier_->RemoveObserver(observer); primary_verifier_->RemoveObserver(observer); } void TrialComparisonCertVerifier::UpdateVerifyProcData( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) { - primary_verifier_->UpdateVerifyProcData(cert_net_fetcher, crl_set, - root_store_data); - primary_reverifier_->UpdateVerifyProcData(cert_net_fetcher, crl_set, - root_store_data); - trial_verifier_->UpdateVerifyProcData(cert_net_fetcher, crl_set, - root_store_data); + const CertVerifyProcFactory::ImplParams& impl_params) { + bool previous_actual_use_chrome_root_store = actual_use_chrome_root_store_; + auto [primary_impl_params, trial_impl_params] = + ProcessImplParams(impl_params); + + // If the only change in the params was to switch the + // `actual_use_chrome_root_store_` value, updating the underlying verifiers + // is unnecessary, but it's probably not worth the trouble to try to avoid + // it. + primary_verifier_->UpdateVerifyProcData(cert_net_fetcher, + primary_impl_params); + primary_reverifier_->UpdateVerifyProcData(cert_net_fetcher, + primary_impl_params); + + trial_verifier_->UpdateVerifyProcData(cert_net_fetcher, trial_impl_params); + // The TrialComparisonCertVerifier is registered as an observer of the // underlying verifiers, and so the OnCertVerifierChanged method should be // triggered to call NotifyJobsOfConfigChange, so it isn't explicitly called - // here. + // here for cases other than the `actual_use_chrome_root_store_` changing. + + if (previous_actual_use_chrome_root_store != actual_use_chrome_root_store_) { + NotifyJobsOfConfigChange(); + } } void TrialComparisonCertVerifier::RemoveJob(Job* job_ptr) { @@ -563,6 +601,18 @@ void TrialComparisonCertVerifier::RemoveJob(Job* job_ptr) { jobs_.erase(it); } +std::tuple<CertVerifyProcFactory::ImplParams, CertVerifyProcFactory::ImplParams> +TrialComparisonCertVerifier::ProcessImplParams( + const CertVerifyProcFactory::ImplParams& impl_params) { + actual_use_chrome_root_store_ = impl_params.use_chrome_root_store; + + CertVerifyProcFactory::ImplParams primary_impl_params(impl_params); + primary_impl_params.use_chrome_root_store = false; + CertVerifyProcFactory::ImplParams trial_impl_params(impl_params); + trial_impl_params.use_chrome_root_store = true; + return {std::move(primary_impl_params), std::move(trial_impl_params)}; +} + void TrialComparisonCertVerifier::NotifyJobsOfConfigChange() { for (auto& job : jobs_) { job->OnConfigChanged(); diff --git a/net/cert/trial_comparison_cert_verifier.h b/net/cert/trial_comparison_cert_verifier.h index b0306c255..1712a81b0 100644 --- a/net/cert/trial_comparison_cert_verifier.h +++ b/net/cert/trial_comparison_cert_verifier.h @@ -19,11 +19,8 @@ #include "net/cert/cert_verifier.h" namespace net { -class CertVerifyProc; class CertVerifyProcFactory; class CertNetFetcher; -class ChromeRootStoreData; -class CRLSet; // TrialComparisonCertVerifier is a CertVerifier that can be used to compare // the results between two different CertVerifyProcs. The results are reported @@ -45,33 +42,35 @@ class NET_EXPORT TrialComparisonCertVerifier const net::CertVerifyResult& primary_result, const net::CertVerifyResult& trial_result)>; - // Create a new TrialComparisonCertVerifier. Initially, no trial - // verifications will actually be performed; that is, calls to Verify() will - // be dispatched to the underlying |primary_verify_proc|. This can be changed - // by calling set_trial_allowed(). + // Create a new TrialComparisonCertVerifier. The `verify_proc_factory` will + // be used to create the underlying primary and trial verifiers. + // + // Initially, no trial verifications will actually be performed; that is, + // calls to Verify() will be dispatched to the underlying `primary_verifier_` + // or `trial_verifier_` depending on the `impl_params`. This can be changed + // by calling `set_trial_allowed()`. // // When trial verifications are enabled, calls to Verify() will first call - // into |primary_verify_proc| to verify. The result of this verification will + // into `primary_verifier_` to verify. The result of this verification will // be immediately returned to the caller of Verify, allowing them to proceed. // However, the verifier will continue in the background, attempting to - // verify the same RequestParams using |trial_verify_proc|. If there are - // differences in the results, they will be reported via |report_callback|, + // verify the same RequestParams using `trial_verifier_`. If there are + // differences in the results, they will be reported via `report_callback`, // allowing the creator to receive information about differences. // // If the caller abandons the CertVerifier::Request prior to the primary // verification completed, no trial verification will be done. However, once // the primary verifier has returned, the trial verifications will continue, // provided that the underlying configuration has not been changed by - // calling SetConfig(). + // calling `SetConfig()` or `UpdateVerifyProcData()`. // - // Note that there may be multiple calls to both |primary_verify_proc| and - // |trial_verify_proc|, using different parameters to account for platform - // differences. + // Note that there may be multiple calls to both the primary CertVerifyProc + // and trial CertVerifyProc, using different parameters to account for + // platform differences. TrialComparisonCertVerifier( - scoped_refptr<CertVerifyProc> primary_verify_proc, - scoped_refptr<CertVerifyProcFactory> primary_verify_proc_factory, - scoped_refptr<CertVerifyProc> trial_verify_proc, - scoped_refptr<CertVerifyProcFactory> trial_verify_proc_factory, + scoped_refptr<CertVerifyProcFactory> verify_proc_factory, + scoped_refptr<CertNetFetcher> cert_net_fetcher, + const CertVerifyProcFactory::ImplParams& impl_params, ReportCallback report_callback); TrialComparisonCertVerifier(const TrialComparisonCertVerifier&) = delete; @@ -94,8 +93,7 @@ class NET_EXPORT TrialComparisonCertVerifier void RemoveObserver(Observer* observer) override; void UpdateVerifyProcData( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) override; + const CertVerifyProcFactory::ImplParams& impl_params) override; private: class Job; @@ -108,6 +106,13 @@ class NET_EXPORT TrialComparisonCertVerifier void RemoveJob(Job* job_ptr); void NotifyJobsOfConfigChange(); + // Processes the params from the caller, updates the state of the trial and + // returns the pair of {primary verifier params, trial verifier params} to + // be used in creating or updating the wrapped verifiers. + std::tuple<CertVerifyProcFactory::ImplParams, + CertVerifyProcFactory::ImplParams> + ProcessImplParams(const CertVerifyProcFactory::ImplParams& impl_params); + // CertVerifier::Observer methods: void OnCertVerifierChanged() override; @@ -116,6 +121,11 @@ class NET_EXPORT TrialComparisonCertVerifier // Callback that reports are sent to. ReportCallback report_callback_; + // The actual `use_chrome_root_store` value that is requested by the caller. + // This determines which verifier's result is returned to the caller as the + // actual verification result. + bool actual_use_chrome_root_store_; + CertVerifier::Config config_; std::unique_ptr<CertVerifierWithUpdatableProc> primary_verifier_; diff --git a/net/cert/trial_comparison_cert_verifier_unittest.cc b/net/cert/trial_comparison_cert_verifier_unittest.cc index ece77e860..859ee6eb8 100644 --- a/net/cert/trial_comparison_cert_verifier_unittest.cc +++ b/net/cert/trial_comparison_cert_verifier_unittest.cc @@ -205,6 +205,10 @@ int NotCalledCertVerifyProc::VerifyInternal( return ERR_UNEXPECTED; } +scoped_refptr<CertVerifyProc> MakeNotCalledProc() { + return base::MakeRefCounted<NotCalledCertVerifyProc>(); +} + void NotCalledCallback(int error) { ADD_FAILURE() << "NotCalledCallback was called with error code " << error; } @@ -232,27 +236,42 @@ class MockCertVerifyProc : public CertVerifyProc { ~MockCertVerifyProc() override = default; }; -class SwapWithNewProcFactory : public CertVerifyProcFactory { +class TestProcFactory : public CertVerifyProcFactory { public: - explicit SwapWithNewProcFactory(scoped_refptr<CertVerifyProc> new_proc) - : verify_proc_(std::move(new_proc)) {} + explicit TestProcFactory( + std::deque<scoped_refptr<CertVerifyProc>> primary_verify_procs, + std::deque<scoped_refptr<CertVerifyProc>> trial_verify_procs) + : primary_verify_procs_(std::move(primary_verify_procs)), + trial_verify_procs_(std::move(trial_verify_procs)) {} scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc( scoped_refptr<CertNetFetcher> cert_net_fetcher, - scoped_refptr<CRLSet> crl_set, - const ChromeRootStoreData* root_store_data) override { - return verify_proc_; + const ImplParams& impl_params) override { + std::deque<scoped_refptr<CertVerifyProc>>* procs = + impl_params.use_chrome_root_store ? &trial_verify_procs_ + : &primary_verify_procs_; + if (procs->empty()) { + ADD_FAILURE() << "procs is empty for crs=" + << impl_params.use_chrome_root_store; + return MakeNotCalledProc(); + } + scoped_refptr<CertVerifyProc> r = procs->front(); + procs->pop_front(); + return r; } protected: - ~SwapWithNewProcFactory() override = default; + ~TestProcFactory() override = default; - scoped_refptr<CertVerifyProc> verify_proc_; + std::deque<scoped_refptr<CertVerifyProc>> primary_verify_procs_; + std::deque<scoped_refptr<CertVerifyProc>> trial_verify_procs_; }; -scoped_refptr<CertVerifyProcFactory> SwapWithNotCalledProcFactory() { - return base::MakeRefCounted<SwapWithNewProcFactory>( - base::MakeRefCounted<NotCalledCertVerifyProc>()); +scoped_refptr<CertVerifyProcFactory> ProcFactory( + std::deque<scoped_refptr<CertVerifyProc>> primary_verify_procs, + std::deque<scoped_refptr<CertVerifyProc>> trial_verify_procs) { + return base::MakeRefCounted<TestProcFactory>(std::move(primary_verify_procs), + std::move(trial_verify_procs)); } struct TrialReportInfo { @@ -332,6 +351,9 @@ class TrialComparisonCertVerifierTest : public TestWithTaskEnvironment { GetTestCertsDirectory(), "lets-encrypt-isrg-x1-root.pem", X509Certificate::FORMAT_AUTO); ASSERT_TRUE(lets_encrypt_isrg_x1_); + + no_crs_impl_params_.use_chrome_root_store = false; + yes_crs_impl_params_.use_chrome_root_store = true; } protected: @@ -340,22 +362,26 @@ class TrialComparisonCertVerifierTest : public TestWithTaskEnvironment { scoped_refptr<X509Certificate> leaf_cert_1_; scoped_refptr<X509Certificate> lets_encrypt_dst_x3_; scoped_refptr<X509Certificate> lets_encrypt_isrg_x1_; + net::CertVerifyProcFactory::ImplParams no_crs_impl_params_; + net::CertVerifyProcFactory::ImplParams yes_crs_impl_params_; base::HistogramTester histograms_; }; -TEST_F(TrialComparisonCertVerifierTest, ObserverIsCalledOnCRSUpdate) { +TEST_F(TrialComparisonCertVerifierTest, ObserverIsCalledOnVerifierUpdate) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({MakeNotCalledProc(), MakeNotCalledProc(), + MakeNotCalledProc(), MakeNotCalledProc()}, + {MakeNotCalledProc(), MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); CertVerifierObserverCounter observer_(&verifier); EXPECT_EQ(observer_.change_count(), 0u); - verifier.UpdateVerifyProcData(nullptr, nullptr, nullptr); - EXPECT_EQ(observer_.change_count(), 1u); + verifier.UpdateVerifyProcData(nullptr, no_crs_impl_params_); + // Observer is called twice since the TrialComparisonCertVerifier currently + // forwards notifications from both the primary and secondary verifiers. + EXPECT_EQ(observer_.change_count(), 2u); } TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowed) { @@ -365,10 +391,10 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowed) { auto verify_proc = base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result); std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc, SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc, MakeNotCalledProc()}, {MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); + CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, /*ocsp_response=*/std::string(), /*sct_list=*/std::string()); @@ -428,9 +454,8 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowedThenAllowed) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0, /*ocsp_response=*/std::string(), @@ -512,9 +537,8 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyAllowedThenDisallowed) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0, @@ -565,6 +589,186 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyAllowedThenDisallowed) { EXPECT_EQ("t0.test", report.hostname); } +TEST_F(TrialComparisonCertVerifierTest, InitiallyCRSEnabledThenDisabled) { + // Certificate that has multiple subjectAltName entries. This allows easily + // confirming which verification attempt the report was generated for without + // having to mock different CertVerifyProc results for each. + base::FilePath certs_dir = + GetTestNetDataDirectory() + .AppendASCII("verify_certificate_chain_unittest") + .AppendASCII("many-names"); + scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile( + certs_dir, "ok-all-types.pem", X509Certificate::FORMAT_AUTO); + ASSERT_TRUE(cert_chain); + ASSERT_EQ(2U, cert_chain->intermediate_buffers().size()); + + scoped_refptr<X509Certificate> leaf = X509Certificate::CreateFromBuffer( + bssl::UpRef(cert_chain->cert_buffer()), {}); + ASSERT_TRUE(leaf); + + CertVerifyResult primary_result; + primary_result.verified_cert = cert_chain; + scoped_refptr<FakeCertVerifyProc> verify_proc1 = + base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result); + + // Trial verifier returns an error status. + CertVerifyResult secondary_result; + secondary_result.cert_status = CERT_STATUS_DATE_INVALID; + secondary_result.verified_cert = cert_chain; + scoped_refptr<FakeCertVerifyProc> verify_proc2 = + base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID, + secondary_result); + + std::vector<TrialReportInfo> reports; + // Verifier created with ImplParams that have use_chrome_root_store=true. + TrialComparisonCertVerifier verifier( + ProcFactory({verify_proc1, MakeNotCalledProc(), verify_proc1, + MakeNotCalledProc()}, + {verify_proc2, verify_proc2}), + nullptr, yes_crs_impl_params_, + base::BindRepeating(&RecordTrialReport, &reports)); + verifier.set_trial_allowed(true); + + CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()); + CertVerifyResult result; + TestCompletionCallback callback; + std::unique_ptr<CertVerifier::Request> request; + int error = verifier.Verify(params, &result, callback.callback(), &request, + NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request); + error = callback.WaitForResult(); + // The actual result returned should be from the secondary verifier. + EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID)); + + // Turn chrome root store off and verify again. + verifier.UpdateVerifyProcData(nullptr, no_crs_impl_params_); + CertVerifier::RequestParams params2(leaf, "t1.test", /*flags=*/0, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()); + CertVerifyResult result2; + TestCompletionCallback callback2; + std::unique_ptr<CertVerifier::Request> request2; + error = verifier.Verify(params2, &result2, callback2.callback(), &request2, + NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request2); + + // The actual result returned should now be from the primary verifier. + error = callback2.WaitForResult(); + EXPECT_THAT(error, IsOk()); + + verify_proc2->WaitForVerifyCall(); + RunUntilIdle(); + + // Primary verifier should have run once, secondary verifier should run twice. + EXPECT_EQ(1, verify_proc1->num_verifications()); + EXPECT_EQ(2, verify_proc2->num_verifications()); + // Trial comparison was only run once. + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1); + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", + 1); + histograms_.ExpectUniqueSample( + "Net.CertVerifier_TrialComparisonResult", + TrialComparisonResult::kPrimaryValidSecondaryError, 1); + + // Expect a report from the second verification. + ASSERT_EQ(1U, reports.size()); + const TrialReportInfo& report = reports[0]; + EXPECT_EQ("t1.test", report.hostname); +} + +TEST_F(TrialComparisonCertVerifierTest, InitiallyAllowedThenCRSEnabled) { + // Certificate that has multiple subjectAltName entries. This allows easily + // confirming which verification attempt the report was generated for without + // having to mock different CertVerifyProc results for each. + base::FilePath certs_dir = + GetTestNetDataDirectory() + .AppendASCII("verify_certificate_chain_unittest") + .AppendASCII("many-names"); + scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile( + certs_dir, "ok-all-types.pem", X509Certificate::FORMAT_AUTO); + ASSERT_TRUE(cert_chain); + ASSERT_EQ(2U, cert_chain->intermediate_buffers().size()); + + scoped_refptr<X509Certificate> leaf = X509Certificate::CreateFromBuffer( + bssl::UpRef(cert_chain->cert_buffer()), {}); + ASSERT_TRUE(leaf); + + CertVerifyResult primary_result; + primary_result.verified_cert = cert_chain; + scoped_refptr<FakeCertVerifyProc> verify_proc1 = + base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result); + + // Trial verifier returns an error status. + CertVerifyResult secondary_result; + secondary_result.cert_status = CERT_STATUS_DATE_INVALID; + secondary_result.verified_cert = cert_chain; + scoped_refptr<FakeCertVerifyProc> verify_proc2 = + base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID, + secondary_result); + + std::vector<TrialReportInfo> reports; + TrialComparisonCertVerifier verifier( + ProcFactory({verify_proc1, MakeNotCalledProc(), verify_proc1, + MakeNotCalledProc()}, + {verify_proc2, verify_proc2}), + nullptr, no_crs_impl_params_, + base::BindRepeating(&RecordTrialReport, &reports)); + verifier.set_trial_allowed(true); + + CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()); + CertVerifyResult result; + TestCompletionCallback callback; + std::unique_ptr<CertVerifier::Request> request; + int error = verifier.Verify(params, &result, callback.callback(), &request, + NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request); + error = callback.WaitForResult(); + EXPECT_THAT(error, IsOk()); + + // Turn chrome root store on and verify again. + verifier.UpdateVerifyProcData(nullptr, yes_crs_impl_params_); + CertVerifier::RequestParams params2(leaf, "t1.test", /*flags=*/0, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()); + CertVerifyResult result2; + TestCompletionCallback callback2; + std::unique_ptr<CertVerifier::Request> request2; + error = verifier.Verify(params2, &result2, callback2.callback(), &request2, + NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request2); + + // The actual result returned should now be from the secondary verifier. + error = callback2.WaitForResult(); + EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID)); + + verify_proc2->WaitForVerifyCall(); + RunUntilIdle(); + + // Primary verifier should have run once, secondary verifier should run twice. + EXPECT_EQ(1, verify_proc1->num_verifications()); + EXPECT_EQ(2, verify_proc2->num_verifications()); + // Trial comparison was only run once. + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1); + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", + 1); + histograms_.ExpectUniqueSample( + "Net.CertVerifier_TrialComparisonResult", + TrialComparisonResult::kPrimaryValidSecondaryError, 1); + + // Expect a report from the first verification. + ASSERT_EQ(1U, reports.size()); + const TrialReportInfo& report = reports[0]; + EXPECT_EQ("t0.test", report.hostname); +} + TEST_F(TrialComparisonCertVerifierTest, ConfigChangedDuringPrimaryVerification) { CertVerifyResult primary_result; @@ -574,9 +778,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc()}, {MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -630,17 +833,18 @@ TEST_F(TrialComparisonCertVerifierTest, ConfigChangedBeforeVerification) { // Both verifiers are initially NotCalledCertVerifyProc, but should swap to // verify_proc1 and verify_proc2 when UpdateVerifyProcData is called. TrialComparisonCertVerifier verifier( - base::MakeRefCounted<NotCalledCertVerifyProc>(), - base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc1), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc2), + ProcFactory({MakeNotCalledProc(), MakeNotCalledProc(), verify_proc1, + MakeNotCalledProc()}, + {MakeNotCalledProc(), verify_proc2}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); + verifier.set_trial_allowed(true); // Change the verifier Chrome Root Store data before verification, so the // Verify should call the verifiers that were swapped in by the factories // instead of the initial ones. - verifier.UpdateVerifyProcData(nullptr, nullptr, nullptr); + verifier.UpdateVerifyProcData(nullptr, no_crs_impl_params_); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, /*ocsp_response=*/std::string(), @@ -696,9 +900,10 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc(), MakeNotCalledProc(), + MakeNotCalledProc()}, + {MakeNotCalledProc(), MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -715,7 +920,7 @@ TEST_F(TrialComparisonCertVerifierTest, // Change the verifier Chrome Root Store data before the primary verification // finishes. - verifier.UpdateVerifyProcData(nullptr, nullptr, nullptr); + verifier.UpdateVerifyProcData(nullptr, no_crs_impl_params_); error = callback.WaitForResult(); EXPECT_THAT(error, IsOk()); @@ -749,9 +954,8 @@ TEST_F(TrialComparisonCertVerifierTest, ConfigChangedDuringTrialVerification) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -811,8 +1015,10 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc(), MakeNotCalledProc(), + MakeNotCalledProc()}, + {verify_proc2, MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -831,7 +1037,7 @@ TEST_F(TrialComparisonCertVerifierTest, EXPECT_THAT(error, IsOk()); // Change the verifier Chrome Root Store data during the trial verification. - verifier.UpdateVerifyProcData(nullptr, nullptr, nullptr); + verifier.UpdateVerifyProcData(nullptr, no_crs_impl_params_); RunUntilIdle(); @@ -864,9 +1070,8 @@ TEST_F(TrialComparisonCertVerifierTest, SameResult) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -914,9 +1119,8 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierErrorSecondaryOk) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -980,9 +1184,8 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierOkSecondaryError) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1045,9 +1248,8 @@ TEST_F(TrialComparisonCertVerifierTest, BothVerifiersDifferentErrors) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1106,9 +1308,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, verify_proc1}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1170,9 +1371,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1238,9 +1438,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, verify_proc1}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1335,9 +1534,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, verify_proc1}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf, "test.example", /*flags=*/0, @@ -1389,9 +1587,8 @@ TEST_F(TrialComparisonCertVerifierTest, BothVerifiersOkDifferentCertStatus) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::Config config; @@ -1462,9 +1659,8 @@ TEST_F(TrialComparisonCertVerifierTest, CancelledDuringPrimaryVerification) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1522,9 +1718,8 @@ TEST_F(TrialComparisonCertVerifierTest, DeletedDuringPrimaryVerification) { std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc()}, {MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); @@ -1571,9 +1766,8 @@ TEST_F(TrialComparisonCertVerifierTest, DeletedDuringVerificationResult) { std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, SwapWithNotCalledProcFactory(), - base::MakeRefCounted<NotCalledCertVerifyProc>(), - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc()}, {MakeNotCalledProc()}), + nullptr, no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); @@ -1636,8 +1830,8 @@ TEST_F(TrialComparisonCertVerifierTest, DeletedDuringTrialReport) { bool was_report_callback_called = false; std::unique_ptr<TrialComparisonCertVerifier> verifier; verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindLambdaForTesting( [&verifier, &was_report_callback_called]( const std::string& hostname, @@ -1702,9 +1896,8 @@ TEST_F(TrialComparisonCertVerifierTest, DeletedAfterTrialVerificationStarted) { std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1769,9 +1962,8 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryRevokedSecondaryOk) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1842,9 +2034,8 @@ TEST_F(TrialComparisonCertVerifierTest, MultipleEVPolicies) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1908,9 +2099,8 @@ TEST_F(TrialComparisonCertVerifierTest, MultipleEVPoliciesNoneValidForRoot) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1978,9 +2168,8 @@ TEST_F(TrialComparisonCertVerifierTest, MultiplePoliciesOnlyOneIsEV) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2031,9 +2220,8 @@ TEST_F(TrialComparisonCertVerifierTest, LocallyTrustedLeaf) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2085,9 +2273,8 @@ TEST_F(TrialComparisonCertVerifierTest, SHA1Ignored) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2140,9 +2327,8 @@ TEST_F(TrialComparisonCertVerifierTest, BothAuthorityInvalidIgnored) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2196,9 +2382,8 @@ TEST_F(TrialComparisonCertVerifierTest, BothKnownRootsIgnored) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2254,9 +2439,8 @@ TEST_F(TrialComparisonCertVerifierTest, std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2305,9 +2489,8 @@ TEST_F(TrialComparisonCertVerifierTest, LetsEncryptSpecialCase) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -2358,9 +2541,8 @@ TEST_F(TrialComparisonCertVerifierTest, AndroidPreferDate) { std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, - SwapWithNotCalledProcFactory(), - base::BindRepeating(&RecordTrialReport, &reports)); + ProcFactory({verify_proc1, MakeNotCalledProc()}, {verify_proc2}), nullptr, + no_crs_impl_params_, base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, diff --git a/net/cookies/cookie_inclusion_status.h b/net/cookies/cookie_inclusion_status.h index 51fb970be..129b13ab7 100644 --- a/net/cookies/cookie_inclusion_status.h +++ b/net/cookies/cookie_inclusion_status.h @@ -5,7 +5,10 @@ #ifndef NET_COOKIES_COOKIE_INCLUSION_STATUS_H_ #define NET_COOKIES_COOKIE_INCLUSION_STATUS_H_ +#include <stdint.h> + #include <bitset> +#include <cstdint> #include <ostream> #include <string> #include <vector> diff --git a/net/cookies/cookie_monster_change_dispatcher.cc b/net/cookies/cookie_monster_change_dispatcher.cc index e1eb9d0f7..36dd9f96c 100644 --- a/net/cookies/cookie_monster_change_dispatcher.cc +++ b/net/cookies/cookie_monster_change_dispatcher.cc @@ -36,14 +36,15 @@ CookieMonsterChangeDispatcher::Subscription::Subscription( std::string domain_key, std::string name_key, GURL url, - absl::optional<CookiePartitionKey> cookie_partition_key, + CookiePartitionKeyCollection cookie_partition_key_collection, bool same_party_attribute_enabled, net::CookieChangeCallback callback) : change_dispatcher_(std::move(change_dispatcher)), domain_key_(std::move(domain_key)), name_key_(std::move(name_key)), url_(std::move(url)), - cookie_partition_key_(std::move(cookie_partition_key)), + cookie_partition_key_collection_( + std::move(cookie_partition_key_collection)), callback_(std::move(callback)), same_party_attribute_enabled_(same_party_attribute_enabled), task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) { @@ -87,14 +88,22 @@ void CookieMonsterChangeDispatcher::Subscription::DispatchChange( } } - if (!change.cookie.IsPartitioned() && - net::CookiePartitionKey::HasNonce(cookie_partition_key_)) { - return; - } - - if (change.cookie.IsPartitioned() && - change.cookie.PartitionKey() != cookie_partition_key_) { - return; + if (!cookie_partition_key_collection_.ContainsAllKeys()) { + if (cookie_partition_key_collection_.PartitionKeys().empty()) { + if (cookie.IsPartitioned()) { + return; + } + } else { + DCHECK_EQ(1u, cookie_partition_key_collection_.PartitionKeys().size()); + const CookiePartitionKey& key = + *cookie_partition_key_collection_.PartitionKeys().begin(); + if (CookiePartitionKey::HasNonce(key) && !cookie.IsPartitioned()) { + return; + } + if (cookie.IsPartitioned() && key != *cookie.PartitionKey()) { + return; + } + } } // TODO(mmenke, pwnall): Run callbacks synchronously? @@ -155,7 +164,8 @@ CookieMonsterChangeDispatcher::AddCallbackForCookie( std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>( weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url, - cookie_partition_key, same_party_attribute_enabled_, std::move(callback)); + CookiePartitionKeyCollection::FromOptional(cookie_partition_key), + same_party_attribute_enabled_, std::move(callback)); LinkSubscription(subscription.get()); return subscription; @@ -170,7 +180,8 @@ CookieMonsterChangeDispatcher::AddCallbackForUrl( std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>( weak_ptr_factory_.GetWeakPtr(), DomainKey(url), - std::string(kGlobalNameKey), url, cookie_partition_key, + std::string(kGlobalNameKey), url, + CookiePartitionKeyCollection::FromOptional(cookie_partition_key), same_party_attribute_enabled_, std::move(callback)); LinkSubscription(subscription.get()); @@ -184,7 +195,8 @@ CookieMonsterChangeDispatcher::AddCallbackForAllChanges( std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>( weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey), - std::string(kGlobalNameKey), GURL(""), absl::nullopt, + std::string(kGlobalNameKey), GURL(""), + CookiePartitionKeyCollection::ContainsAll(), same_party_attribute_enabled_, std::move(callback)); LinkSubscription(subscription.get()); diff --git a/net/cookies/cookie_monster_change_dispatcher.h b/net/cookies/cookie_monster_change_dispatcher.h index dd022adb2..31ca1e979 100644 --- a/net/cookies/cookie_monster_change_dispatcher.h +++ b/net/cookies/cookie_monster_change_dispatcher.h @@ -18,6 +18,7 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "net/cookies/cookie_change_dispatcher.h" +#include "net/cookies/cookie_partition_key_collection.h" #include "url/gurl.h" namespace net { @@ -77,7 +78,7 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher { std::string domain_key, std::string name_key, GURL url, - absl::optional<CookiePartitionKey> cookie_partition_key, + CookiePartitionKeyCollection cookie_partition_key_collection, bool same_party_attribute_enabled, net::CookieChangeCallback callback); @@ -104,8 +105,7 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher { const std::string domain_key_; // kGlobalDomainKey means no filtering. const std::string name_key_; // kGlobalNameKey means no filtering. const GURL url_; // empty() means no URL-based filtering. - // nullopt means all Partitioned cookies will be ignored. - const absl::optional<CookiePartitionKey> cookie_partition_key_; + const CookiePartitionKeyCollection cookie_partition_key_collection_; const net::CookieChangeCallback callback_; bool same_party_attribute_enabled_; diff --git a/net/cookies/cookie_store_change_unittest.h b/net/cookies/cookie_store_change_unittest.h index 08b0d0643..be89df1f3 100644 --- a/net/cookies/cookie_store_change_unittest.h +++ b/net/cookies/cookie_store_change_unittest.h @@ -707,6 +707,38 @@ TYPED_TEST_P(CookieStoreChangeGlobalTest, ChangeIncludesCookieAccessSemantics) { cookie_changes[3].access_result.access_semantics)); } +TYPED_TEST_P(CookieStoreChangeGlobalTest, PartitionedCookies) { + if (!TypeParam::supports_named_cookie_tracking || + !TypeParam::supports_partitioned_cookies) { + return; + } + + CookieStore* cs = this->GetCookieStore(); + + // Test that all partitioned cookies are visible to global change listeners. + std::vector<CookieChangeInfo> all_cookie_changes; + std::unique_ptr<CookieChangeSubscription> global_subscription = + cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( + &CookieStoreChangeTestBase<TypeParam>::OnCookieChange, + base::Unretained(&all_cookie_changes))); + // Set two cookies in two separate partitions, one with nonce. + this->CreateAndSetCookie( + cs, GURL("https://www.example2.com"), + "__Host-a=1; Secure; Path=/; Partitioned", + CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))); + this->CreateAndSetCookie( + cs, GURL("https://www.example2.com"), + "__Host-a=2; Secure; Path=/; Partitioned; Max-Age=7200", + CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"), + base::UnguessableToken::Create())); + this->DeliverChangeNotifications(); + ASSERT_EQ(2u, all_cookie_changes.size()); +} + TYPED_TEST_P(CookieStoreChangeUrlTest, NoCookie) { if (!TypeParam::supports_url_cookie_tracking) return; @@ -1748,16 +1780,14 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, PartitionedCookies) { "__Host-b=2; Secure; Path=/; Partitioned", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com"))); // Partitioned cookie with a different partition key this->CreateAndSetCookie( cs, GURL("https://www.example.com"), "__Host-c=3; Secure; Path=/; Partitioned", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"))); this->DeliverChangeNotifications(); ASSERT_EQ(2u, cookie_changes.size()); @@ -1785,14 +1815,61 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, PartitionedCookies) { "__Host-b=2; Secure; Path=/; Partitioned; Max-Age=7200", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, other_cookie_changes.size()); // Check that the other listener was invoked. ASSERT_LT(2u, cookie_changes.size()); } +TYPED_TEST_P(CookieStoreChangeUrlTest, PartitionedCookies_WithNonce) { + if (!TypeParam::supports_named_cookie_tracking || + !TypeParam::supports_partitioned_cookies) { + return; + } + + CookieStore* cs = this->GetCookieStore(); + base::UnguessableToken nonce = base::UnguessableToken::Create(); + + std::vector<CookieChangeInfo> cookie_changes; + std::unique_ptr<CookieChangeSubscription> subscription = + cs->GetChangeDispatcher().AddCallbackForUrl( + GURL("https://www.example.com"), + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"), + nonce), + base::BindRepeating( + &CookieStoreChangeTestBase<TypeParam>::OnCookieChange, + base::Unretained(&cookie_changes))); + + // Should not see changes to an unpartitioned cookie. + this->CreateAndSetCookie(cs, GURL("https://www.example.com"), + "__Host-a=1; Secure; Path=/", + CookieOptions::MakeAllInclusive()); + this->DeliverChangeNotifications(); + ASSERT_EQ(0u, cookie_changes.size()); + + // Set partitioned cookie without nonce. Should not see the change. + this->CreateAndSetCookie( + cs, GURL("https://www.example.com"), + "__Host-a=2; Secure; Path=/; Partitioned", + CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))); + this->DeliverChangeNotifications(); + ASSERT_EQ(0u, cookie_changes.size()); + + // Set partitioned cookie with nonce. + this->CreateAndSetCookie(cs, GURL("https://www.example.com"), + "__Host-a=3; Secure; Path=/; Partitioned", + CookieOptions::MakeAllInclusive(), + absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting( + GURL("https://www.foo.com"), nonce)); + this->DeliverChangeNotifications(); + ASSERT_EQ(1u, cookie_changes.size()); +} + TYPED_TEST_P(CookieStoreChangeNamedTest, NoCookie) { if (!TypeParam::supports_named_cookie_tracking) return; @@ -2987,16 +3064,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, PartitionedCookies) { "__Host-a=2; Secure; Path=/; Partitioned", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com"))); // Partitioned cookie with a different partition key this->CreateAndSetCookie( cs, GURL("https://www.example.com"), "__Host-a=3; Secure; Path=/; Partitioned", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"))); this->DeliverChangeNotifications(); ASSERT_EQ(2u, cookie_changes.size()); @@ -3023,14 +3098,61 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, PartitionedCookies) { "__Host-a=2; Secure; Path=/; Partitioned; Max-Age=7200", CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, absl::nullopt /* system_time */, - absl::make_optional( - CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")))); + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, other_cookie_changes.size()); // Check that the other listener was invoked. ASSERT_LT(2u, cookie_changes.size()); } +TYPED_TEST_P(CookieStoreChangeNamedTest, PartitionedCookies_WithNonce) { + if (!TypeParam::supports_named_cookie_tracking || + !TypeParam::supports_partitioned_cookies) { + return; + } + + CookieStore* cs = this->GetCookieStore(); + base::UnguessableToken nonce = base::UnguessableToken::Create(); + + std::vector<CookieChangeInfo> cookie_changes; + std::unique_ptr<CookieChangeSubscription> subscription = + cs->GetChangeDispatcher().AddCallbackForCookie( + GURL("https://www.example.com"), "__Host-a", + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"), + nonce), + base::BindRepeating( + &CookieStoreChangeTestBase<TypeParam>::OnCookieChange, + base::Unretained(&cookie_changes))); + + // Should not see changes to an unpartitioned cookie. + this->CreateAndSetCookie(cs, GURL("https://www.example.com"), + "__Host-a=1; Secure; Path=/", + CookieOptions::MakeAllInclusive()); + this->DeliverChangeNotifications(); + ASSERT_EQ(0u, cookie_changes.size()); + + // Set partitioned cookie without nonce. Should not see the change. + this->CreateAndSetCookie( + cs, GURL("https://www.example.com"), + "__Host-a=2; Secure; Path=/; Partitioned", + CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))); + this->DeliverChangeNotifications(); + ASSERT_EQ(0u, cookie_changes.size()); + + // Set partitioned cookie with nonce. + this->CreateAndSetCookie(cs, GURL("https://www.example.com"), + "__Host-a=3; Secure; Path=/; Partitioned", + CookieOptions::MakeAllInclusive(), + absl::nullopt /* server_time */, + absl::nullopt /* system_time */, + CookiePartitionKey::FromURLForTesting( + GURL("https://www.foo.com"), nonce)); + this->DeliverChangeNotifications(); + ASSERT_EQ(1u, cookie_changes.size()); +} + REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest, NoCookie, InitialCookie, @@ -3046,7 +3168,8 @@ REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest, DeregisterRace, DeregisterRaceMultiple, MultipleSubscriptions, - ChangeIncludesCookieAccessSemantics); + ChangeIncludesCookieAccessSemantics, + PartitionedCookies); REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest, NoCookie, @@ -3071,7 +3194,8 @@ REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest, DifferentSubscriptionsFiltering, MultipleSubscriptions, ChangeIncludesCookieAccessSemantics, - PartitionedCookies); + PartitionedCookies, + PartitionedCookies_WithNonce); REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest, NoCookie, @@ -3098,7 +3222,8 @@ REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest, MultipleSubscriptions, SubscriptionOutlivesStore, ChangeIncludesCookieAccessSemantics, - PartitionedCookies); + PartitionedCookies, + PartitionedCookies_WithNonce); } // namespace net diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README index fc1523bd4..c56b7e18f 100644 --- a/net/data/ssl/certificates/README +++ b/net/data/ssl/certificates/README @@ -56,8 +56,6 @@ unit tests. - lets-encrypt-isrg-x1-root.pem: A chain that ends in the Lets encrypt ISRG X1 root (https://crt.sh/?id=9314791). Has the same leaf as lets-encrypt-dst-x3-root.pem. -- vrk_gov_root.pem: A root certificate that is marked as a legacy CA in the - known roots list. ===== Manually generated certificates - client.p12 : A PKCS #12 file containing a client certificate and a private diff --git a/net/data/ssl/certificates/vrk_gov_root.pem b/net/data/ssl/certificates/vrk_gov_root.pem deleted file mode 100644 index 078dfc172..000000000 --- a/net/data/ssl/certificates/vrk_gov_root.pem +++ /dev/null @@ -1,84 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 100000 (0x186a0) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C = FI, ST = Finland, O = Vaestorekisterikeskus CA, OU = Certification Authority Services, OU = Varmennepalvelut, CN = VRK Gov. Root CA - Validity - Not Before: Dec 18 13:53:00 2002 GMT - Not After : Dec 18 13:51:08 2023 GMT - Subject: C = FI, ST = Finland, O = Vaestorekisterikeskus CA, OU = Certification Authority Services, OU = Varmennepalvelut, CN = VRK Gov. Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:b0:85:15:da:c8:03:37:d0:a3:46:37:6c:1b:1e: - 96:30:c2:5a:85:12:67:23:f2:bb:9f:e7:8a:81:60: - 27:f8:13:a9:3c:bc:f7:86:aa:aa:f4:f3:25:29:b4: - fe:75:ae:1e:81:86:8a:05:b2:1d:65:b2:38:e8:b4: - cc:28:9a:fb:17:36:f1:93:d5:79:ce:c1:83:8b:21: - 4f:c3:0d:ad:41:df:78:9d:48:e3:1f:42:44:fc:3c: - 6d:21:20:6b:ad:22:84:24:42:8f:17:4d:c2:50:1f: - 64:cd:2d:39:22:56:88:fd:b2:63:9d:54:da:42:69: - c0:c8:4f:d7:18:e2:3e:c8:69:84:94:3d:2c:80:c6: - 7c:ce:bd:d7:53:1f:eb:88:b9:a6:cb:bb:85:57:ef: - 57:76:5d:0c:8b:d3:5e:12:41:9f:21:c0:39:f4:26: - 6d:08:fa:38:b3:a1:77:b1:ee:16:d8:d0:68:da:b4: - 98:a5:a0:65:46:4a:6b:8d:7e:aa:4d:60:b8:f8:c8: - 0d:fc:71:3e:ee:39:87:81:b4:d9:f8:6e:90:ee:3f: - 0e:61:d7:1d:2b:68:e6:2e:e1:42:44:26:78:2c:58: - f2:7d:16:7f:61:c0:49:24:2a:89:87:b6:5d:2f:29: - 19:f8:a6:e7:8e:52:9e:41:4b:5a:0e:aa:b8:c2:66: - 42:53 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - Netscape Cert Type: - SSL CA, S/MIME CA, Object Signing CA - X509v3 Key Usage: critical - Digital Signature, Non Repudiation, Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - DB:E9:E1:9B:D2:D1:24:0B:FC:AB:E3:A0:67:EA:AE:9C:4B:77:F4:B0 - Signature Algorithm: sha1WithRSAEncryption - ad:7d:48:0f:54:11:9e:58:ee:af:0d:9b:12:2f:21:a4:cd:9b: - ba:84:47:e6:c9:25:55:23:e3:df:18:58:2a:2c:db:5e:f7:cd: - 54:f5:51:24:7b:62:67:e1:b1:1f:49:af:34:d0:eb:b1:cc:d9: - a2:0d:52:7f:42:4b:88:60:97:cf:25:72:b7:4f:29:2d:62:9f: - 4f:a1:c0:55:57:56:0e:c4:68:97:91:1f:9c:64:c2:29:32:01: - e9:d4:c8:da:b8:81:98:28:2e:18:c7:2c:fc:eb:9b:52:96:df: - f4:c8:90:19:2d:23:f3:f1:bb:71:da:9e:85:23:bd:1a:ef:2e: - e4:7a:79:b7:c3:9d:86:49:2d:63:b9:2d:74:cf:65:0f:32:66: - 89:df:3b:21:ee:29:6f:39:63:d9:15:c1:6e:f6:df:80:3e:50: - 78:19:8a:dd:03:a3:14:a5:37:a7:b5:2c:7c:b6:11:87:e7:05: - f2:bc:b6:de:d4:ff:97:81:28:84:fe:fe:6c:46:85:10:41:9f: - 4d:75:8c:07:d4:99:67:6f:75:8a:6f:e4:50:92:f6:99:d5:10: - b8:c4:a9:7b:f7:17:8d:4b:bf:d7:95:9f:09:dc:44:0f:1e:32: - c3:c0:cf:d3:79:0d:e4:c7:3b:87:f0:90:34:88:21:62:49:92: - 04:04:1f:bc - ------BEGIN CERTIFICATE----- -MIIEGjCCAwKgAwIBAgIDAYagMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYDVQQGEwJG -STEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0ZXJpa2Vz -a3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBTZXJ2aWNl -czEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJLIEdvdi4g -Um9vdCBDQTAeFw0wMjEyMTgxMzUzMDBaFw0yMzEyMTgxMzUxMDhaMIGjMQswCQYD -VQQGEwJGSTEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0 -ZXJpa2Vza3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBT -ZXJ2aWNlczEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJL -IEdvdi4gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCF -FdrIAzfQo0Y3bBseljDCWoUSZyPyu5/nioFgJ/gTqTy894aqqvTzJSm0/nWuHoGG -igWyHWWyOOi0zCia+xc28ZPVec7Bg4shT8MNrUHfeJ1I4x9CRPw8bSEga60ihCRC -jxdNwlAfZM0tOSJWiP2yY51U2kJpwMhP1xjiPshphJQ9LIDGfM6911Mf64i5psu7 -hVfvV3ZdDIvTXhJBnyHAOfQmbQj6OLOhd7HuFtjQaNq0mKWgZUZKa41+qk1guPjI -DfxxPu45h4G02fhukO4/DmHXHSto5i7hQkQmeCxY8n0Wf2HASSQqiYe2XS8pGfim -545SnkFLWg6quMJmQlMCAwEAAaNVMFMwDwYDVR0TAQH/BAUwAwEB/zARBglghkgB -hvhCAQEEBAMCAAcwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBTb6eGb0tEkC/yr -46Bn6q6cS3f0sDANBgkqhkiG9w0BAQUFAAOCAQEArX1ID1QRnljurw2bEi8hpM2b -uoRH5sklVSPj3xhYKizbXvfNVPVRJHtiZ+GxH0mvNNDrsczZog1Sf0JLiGCXzyVy -t08pLWKfT6HAVVdWDsRol5EfnGTCKTIB6dTI2riBmCguGMcs/OubUpbf9MiQGS0j -8/G7cdqehSO9Gu8u5Hp5t8OdhkktY7ktdM9lDzJmid87Ie4pbzlj2RXBbvbfgD5Q -eBmK3QOjFKU3p7UsfLYRh+cF8ry23tT/l4EohP7+bEaFEEGfTXWMB9SZZ291im/k -UJL2mdUQuMSpe/cXjUu/15WfCdxEDx4yw8DP03kN5Mc7h/CQNIghYkmSBAQfvA== ------END CERTIFICATE----- - diff --git a/net/data/test_support_bundle_data.filelist b/net/data/test_support_bundle_data.filelist index 08d3390ad..a4f984cc6 100644 --- a/net/data/test_support_bundle_data.filelist +++ b/net/data/test_support_bundle_data.filelist @@ -205,7 +205,6 @@ data/ssl/certificates/unittest.key.bin data/ssl/certificates/unittest.selfsigned.der data/ssl/certificates/verisign_intermediate_ca_2011.pem data/ssl/certificates/verisign_intermediate_ca_2016.pem -data/ssl/certificates/vrk_gov_root.pem data/ssl/certificates/weak_digest_md2_ee.pem data/ssl/certificates/weak_digest_md2_intermediate.pem data/ssl/certificates/weak_digest_md2_root.pem diff --git a/net/dns/address_sorter_posix.cc b/net/dns/address_sorter_posix.cc index 060e16d54..0f4afb748 100644 --- a/net/dns/address_sorter_posix.cc +++ b/net/dns/address_sorter_posix.cc @@ -278,18 +278,6 @@ class AddressSorterPosix::SortContext { VLOG(1) << "Could not connect to " << dest.ToStringWithoutPort() << " reason " << rv; sort_list_[info_index].failed = true; - MaybeFinishSort(); - return; - } - // Filter out unusable destinations. - IPEndPoint src; - rv = sort_list_[info_index].socket->GetLocalAddress(&src); - if (rv != OK) { - LOG(WARNING) << "Could not get local address for " - << dest.ToStringWithoutPort() << " reason " << rv; - sort_list_[info_index].failed = true; - MaybeFinishSort(); - return; } MaybeFinishSort(); @@ -303,10 +291,21 @@ class AddressSorterPosix::SortContext { if (num_completed_ != num_endpoints_) { return; } - base::EraseIf(sort_list_, [](auto& element) { return element.failed; }); for (auto& info : sort_list_) { + if (info.failed) { + continue; + } + IPEndPoint src; - info.socket->GetLocalAddress(&src); + // Filter out unusable destinations. + int rv = info.socket->GetLocalAddress(&src); + if (rv != OK) { + LOG(WARNING) << "Could not get local address for " + << info.endpoint.ToStringWithoutPort() << " reason " << rv; + info.failed = true; + continue; + } + auto iter = sorter_->source_map_.find(src.address()); if (iter == sorter_->source_map_.end()) { // |src.address| may not be in the map if |source_info_| has not been @@ -327,6 +326,7 @@ class AddressSorterPosix::SortContext { info.src.prefix_length); } } + base::EraseIf(sort_list_, [](auto& element) { return element.failed; }); std::stable_sort(sort_list_.begin(), sort_list_.end(), CompareDestinations); std::vector<IPEndPoint> sorted_result; diff --git a/net/first_party_sets/addition_overlaps_union_find.h b/net/first_party_sets/addition_overlaps_union_find.h index eeabb6eb8..cd92802d6 100644 --- a/net/first_party_sets/addition_overlaps_union_find.h +++ b/net/first_party_sets/addition_overlaps_union_find.h @@ -28,7 +28,7 @@ class NET_EXPORT AdditionOverlapsUnionFind { // Unions the two given sets together if they are in disjoint sets, and does // nothing if they are non-disjoint. // Unions are non-commutative for First-Party Sets; this method always chooses - // the set with the lesser index as the owner. + // the set with the lesser index as the primary. // Both set indices (set_x, set_y) must be in the range [0, num_sets) where // num_sets is the argument given to the constructor. // If Union is called when num_sets = 0, then this will crash. diff --git a/net/first_party_sets/global_first_party_sets.cc b/net/first_party_sets/global_first_party_sets.cc index ad5d30fa3..51cb3422e 100644 --- a/net/first_party_sets/global_first_party_sets.cc +++ b/net/first_party_sets/global_first_party_sets.cc @@ -275,21 +275,22 @@ FirstPartySetsContextConfig GlobalFirstPartySets::ComputeConfig( FlattenedSets flattened_additions = SetListToFlattenedSets(normalized_additions); - // All of the custom sets are automatically inserted into site_to_owner. + // All of the custom sets are automatically inserted into site_to_entry. UpdateCustomizations(replacement_sets, site_to_entry); UpdateCustomizations(normalized_additions, site_to_entry); - // Maps old owner to new entry. - base::flat_map<SchemefulSite, FirstPartySetEntry> addition_intersected_owners; + // Maps old primary site to new entry. + base::flat_map<SchemefulSite, FirstPartySetEntry> + addition_intersected_primaries; for (const auto& [new_member, new_entry] : flattened_additions) { if (const auto entry = FindEntry(new_member, /*config=*/nullptr); entry.has_value()) { // Found an overlap with the existing list of sets. - addition_intersected_owners.emplace(entry->primary(), new_entry); + addition_intersected_primaries.emplace(entry->primary(), new_entry); } } - // Maps an existing owner to the members it lost due to replacement. + // Maps an existing primary site to the members it lost due to replacement. base::flat_map<SchemefulSite, base::flat_set<SchemefulSite>> potential_singletons; for (const auto& [member, set_entry] : flattened_replacements) { @@ -297,30 +298,30 @@ FirstPartySetsContextConfig GlobalFirstPartySets::ComputeConfig( continue; if (const auto existing_entry = FindEntry(member, /*config=*/nullptr); existing_entry.has_value() && existing_entry->primary() != member && - !addition_intersected_owners.contains(existing_entry->primary()) && + !addition_intersected_primaries.contains(existing_entry->primary()) && !flattened_additions.contains(existing_entry->primary()) && !flattened_replacements.contains(existing_entry->primary())) { potential_singletons[existing_entry->primary()].insert(member); } } - // Find the existing owners that have left their existing sets, and whose - // existing members should be removed from their set (excluding any custom - // sets that those members are involved in). - base::flat_set<SchemefulSite> replaced_existing_owners; - for (const auto& [site, unused_owner] : flattened_replacements) { + // Find the existing primary sites that have left their existing sets, and + // whose existing members should be removed from their set (excluding any + // custom sets that those members are involved in). + base::flat_set<SchemefulSite> replaced_existing_primaries; + for (const auto& [site, unused_primary] : flattened_replacements) { if (const auto entry = FindEntry(site, /*config=*/nullptr); entry.has_value() && entry->primary() == site) { - // Site was an owner in the existing sets. - bool inserted = replaced_existing_owners.emplace(site).second; + // Site was an primary in the existing sets. + bool inserted = replaced_existing_primaries.emplace(site).second; CHECK(inserted); } } - if (!addition_intersected_owners.empty() || !potential_singletons.empty() || - !replaced_existing_owners.empty()) { + if (!addition_intersected_primaries.empty() || + !potential_singletons.empty() || !replaced_existing_primaries.empty()) { // Find out which potential singletons are actually singletons; delete - // members whose owners left; and reparent the sets that intersected with + // members whose primaries left; and reparent the sets that intersected with // an addition set. // Note: use a null config here, to avoid taking unrelated policy sets into // account. @@ -329,8 +330,8 @@ FirstPartySetsContextConfig GlobalFirstPartySets::ComputeConfig( [&](const SchemefulSite& member, const FirstPartySetEntry& set_entry) { // Reparent all sites in any intersecting addition sets. if (const auto entry = - addition_intersected_owners.find(set_entry.primary()); - entry != addition_intersected_owners.end() && + addition_intersected_primaries.find(set_entry.primary()); + entry != addition_intersected_primaries.end() && !flattened_replacements.contains(member)) { site_to_entry.emplace_back( member, FirstPartySetEntry(entry->second.primary(), @@ -345,24 +346,24 @@ FirstPartySetsContextConfig GlobalFirstPartySets::ComputeConfig( if (const auto entry = potential_singletons.find(set_entry.primary()); entry != potential_singletons.end() && !entry->second.contains(member)) { - // This owner lost members, but it still has at least one + // This primary lost members, but it still has at least one // (`member`), so it's not a singleton. potential_singletons.erase(entry); } - // Remove members from sets whose owner left. - if (replaced_existing_owners.contains(set_entry.primary()) && + // Remove members from sets whose primary left. + if (replaced_existing_primaries.contains(set_entry.primary()) && !flattened_replacements.contains(member) && - !addition_intersected_owners.contains(set_entry.primary())) { + !addition_intersected_primaries.contains(set_entry.primary())) { site_to_entry.emplace_back(member, FirstPartySetEntryOverride()); } return true; }); - // Any owner remaining in `potential_singleton` is a real singleton, so + // Any primary remaining in `potential_singleton` is a real singleton, so // delete it: - for (const auto& [owner, members] : potential_singletons) { - site_to_entry.emplace_back(owner, FirstPartySetEntryOverride()); + for (const auto& [primary, members] : potential_singletons) { + site_to_entry.emplace_back(primary, FirstPartySetEntryOverride()); } } diff --git a/net/first_party_sets/global_first_party_sets.h b/net/first_party_sets/global_first_party_sets.h index fdac238f9..cc2b42c89 100644 --- a/net/first_party_sets/global_first_party_sets.h +++ b/net/first_party_sets/global_first_party_sets.h @@ -168,8 +168,8 @@ class NET_EXPORT GlobalFirstPartySets { addition_sets) const; // Returns whether `site` is same-party with `party_context`, and - // `top_frame_site` (if it is not nullptr). That is, is `site`'s owner the - // same as the owners of every member of `party_context` and of + // `top_frame_site` (if it is not nullptr). That is, is `site`'s primary the + // same as the primaries of every member of `party_context` and of // `top_frame_site`? Note: if `site` is not a member of a First-Party Set, // then this returns false. If `top_frame_site` is nullptr, then it is // ignored. diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index 025869ad6..f6f9bf3c0 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -996,8 +996,6 @@ void HttpCache::WritersDoneWritingToEntry(ActiveEntry* entry, DCHECK(entry->writers->IsEmpty()); DCHECK(success || make_readers.empty()); - entry->writers_done_writing_to_entry_history = absl::make_optional(success); - if (!success && should_keep_entry) { // Restart already validated transactions so that they are able to read // the truncated status of the entry. diff --git a/net/http/http_cache.h b/net/http/http_cache.h index 52682432f..ddbcbab14 100644 --- a/net/http/http_cache.h +++ b/net/http/http_cache.h @@ -395,8 +395,6 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory { // True if entry is doomed. bool doomed = false; - - absl::optional<bool> writers_done_writing_to_entry_history; }; using ActiveEntriesMap = diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index d8c1a38e2..d8bade0a8 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -124,6 +124,15 @@ HttpNetworkTransaction::~HttpNetworkTransaction() { // this network transaction was prematurely cancelled. GenerateNetworkErrorLoggingReport(ERR_ABORTED); #endif // BUILDFLAG(ENABLE_REPORTING) + + if (quic_protocol_error_retry_delay_) { + base::UmaHistogramTimes( + IsGoogleHostWithAlpnH3(url_.host()) + ? "Net.QuicProtocolErrorRetryDelayH3SupportedGoogleHost.Failure" + : "Net.QuicProtocolErrorRetryDelay.Failure", + *quic_protocol_error_retry_delay_); + } + if (stream_.get()) { // TODO(mbelshe): The stream_ should be able to compute whether or not the // stream should be kept alive. No reason to compute here @@ -164,8 +173,8 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, request_->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent, &request_user_agent_); request_reporting_upload_depth_ = request_->reporting_upload_depth; - start_timeticks_ = base::TimeTicks::Now(); #endif // BUILDFLAG(ENABLE_REPORTING) + start_timeticks_ = base::TimeTicks::Now(); if (request_->load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES) { server_ssl_config_.disable_cert_verification_network_fetches = true; @@ -1175,7 +1184,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { response_.headers->response_code()); // This will close the socket - it would be weird to try and reuse it, even // if the server doesn't actually close it. - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(RetryReason::kHttpRequestTimeout); return OK; } @@ -1227,7 +1236,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { enable_alternative_services_ = false; net_log_.AddEvent( NetLogEventType::HTTP_TRANSACTION_RESTART_MISDIRECTED_REQUEST); - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(RetryReason::kHttpMisdirectedRequest); return OK; } @@ -1341,6 +1350,15 @@ int HttpNetworkTransaction::DoReadBodyComplete(int result) { #if BUILDFLAG(ENABLE_REPORTING) GenerateNetworkErrorLoggingReport(result); #endif // BUILDFLAG(ENABLE_REPORTING) + + if (result == OK && quic_protocol_error_retry_delay_) { + base::UmaHistogramTimes( + IsGoogleHostWithAlpnH3(url_.host()) + ? "Net.QuicProtocolErrorRetryDelayH3SupportedGoogleHost.Success" + : "Net.QuicProtocolErrorRetryDelay.Success", + *quic_protocol_error_retry_delay_); + quic_protocol_error_retry_delay_.reset(); + } } // Clear these to avoid leaving around old state. @@ -1522,7 +1540,7 @@ int HttpNetworkTransaction::HandleHttp11Required(int error) { // HttpServerProperties should have been updated, so when the request is sent // again, it will automatically use HTTP/1.1. - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(RetryReason::kHttp11Required); return OK; } @@ -1569,7 +1587,8 @@ int HttpNetworkTransaction::HandleSSLClientAuthError(int error) { retry_attempts_++; net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend( + RetryReason::kSslClientAuthSignatureFailed); return OK; } } @@ -1577,6 +1596,44 @@ int HttpNetworkTransaction::HandleSSLClientAuthError(int error) { return error; } +// static +absl::optional<HttpNetworkTransaction::RetryReason> +HttpNetworkTransaction::GetRetryReasonForIOError(int error) { + switch (error) { + case ERR_CONNECTION_RESET: + return RetryReason::kConnectionReset; + case ERR_CONNECTION_CLOSED: + return RetryReason::kConnectionClosed; + case ERR_CONNECTION_ABORTED: + return RetryReason::kConnectionAborted; + case ERR_SOCKET_NOT_CONNECTED: + return RetryReason::kSocketNotConnected; + case ERR_EMPTY_RESPONSE: + return RetryReason::kEmptyResponse; + case ERR_EARLY_DATA_REJECTED: + return RetryReason::kEarlyDataRejected; + case ERR_WRONG_VERSION_ON_EARLY_DATA: + return RetryReason::kWrongVersionOnEarlyData; + case ERR_HTTP2_PING_FAILED: + return RetryReason::kHttp2PingFailed; + case ERR_HTTP2_SERVER_REFUSED_STREAM: + return RetryReason::kHttp2ServerRefusedStream; + case ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE: + return RetryReason::kHttp2PushedStreamNotAvailable; + case ERR_HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER: + return RetryReason::kHttp2ClaimedPushedStreamResetByServer; + case ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH: + return RetryReason::kHttp2PushedResponseDoesNotMatch; + case ERR_QUIC_HANDSHAKE_FAILED: + return RetryReason::kQuicHandshakeFailed; + case ERR_QUIC_GOAWAY_REQUEST_CAN_BE_RETRIED: + return RetryReason::kQuicGoawayRequestCanBeRetried; + case ERR_QUIC_PROTOCOL_ERROR: + return RetryReason::kQuicProtocolError; + } + return absl::nullopt; +} + // This method determines whether it is safe to resend the request after an // IO error. It should only be called in response to errors received before // final set of response headers have been successfully parsed, that the @@ -1591,14 +1648,19 @@ int HttpNetworkTransaction::HandleIOError(int error) { GenerateNetworkErrorLoggingReportIfError(error); #endif // BUILDFLAG(ENABLE_REPORTING) - switch (error) { + absl::optional<HttpNetworkTransaction::RetryReason> retry_reason = + GetRetryReasonForIOError(error); + if (!retry_reason) { + return error; + } + switch (*retry_reason) { // If we try to reuse a connection that the server is in the process of // closing, we may end up successfully writing out our request (or a // portion of our request) only to find a connection error when we try to // read from (or finish writing to) the socket. - case ERR_CONNECTION_RESET: - case ERR_CONNECTION_CLOSED: - case ERR_CONNECTION_ABORTED: + case RetryReason::kConnectionReset: + case RetryReason::kConnectionClosed: + case RetryReason::kConnectionAborted: // There can be a race between the socket pool checking checking whether a // socket is still connected, receiving the FIN, and sending/reading data // on a reused socket. If we receive the FIN between the connectedness @@ -1606,62 +1668,75 @@ int HttpNetworkTransaction::HandleIOError(int error) { // is disconnected when we get a ERR_SOCKET_NOT_CONNECTED. This will most // likely happen when trying to retrieve its IP address. // See http://crbug.com/105824 for more details. - case ERR_SOCKET_NOT_CONNECTED: + case RetryReason::kSocketNotConnected: // If a socket is closed on its initial request, HttpStreamParser returns // ERR_EMPTY_RESPONSE. This may still be close/reuse race if the socket was // preconnected but failed to be used before the server timed it out. - case ERR_EMPTY_RESPONSE: + case RetryReason::kEmptyResponse: if (ShouldResendRequest()) { net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(*retry_reason); error = OK; } break; - case ERR_EARLY_DATA_REJECTED: - case ERR_WRONG_VERSION_ON_EARLY_DATA: + case RetryReason::kEarlyDataRejected: + case RetryReason::kWrongVersionOnEarlyData: net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); // Disable early data on the SSLConfig on a reset. can_send_early_data_ = false; - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(*retry_reason); error = OK; break; - case ERR_HTTP2_PING_FAILED: - case ERR_HTTP2_SERVER_REFUSED_STREAM: - case ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE: - case ERR_HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER: - case ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH: - case ERR_QUIC_HANDSHAKE_FAILED: - case ERR_QUIC_GOAWAY_REQUEST_CAN_BE_RETRIED: + case RetryReason::kHttp2PingFailed: + case RetryReason::kHttp2ServerRefusedStream: + case RetryReason::kHttp2PushedStreamNotAvailable: + case RetryReason::kHttp2ClaimedPushedStreamResetByServer: + case RetryReason::kHttp2PushedResponseDoesNotMatch: + case RetryReason::kQuicHandshakeFailed: + case RetryReason::kQuicGoawayRequestCanBeRetried: if (HasExceededMaxRetries()) break; net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); retry_attempts_++; - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(*retry_reason); error = OK; break; - case ERR_QUIC_PROTOCOL_ERROR: - if (GetResponseHeaders() != nullptr || - !stream_->GetAlternativeService(&retried_alternative_service_)) { + case RetryReason::kQuicProtocolError: + if (GetResponseHeaders() != nullptr) { // If the response headers have already been received and passed up - // then the request can not be retried. Also, if there was no - // alternative service used for this request, then there is no - // alternative service to be disabled. + // then the request can not be retried. + RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus::kNoRetryHeaderReceived); break; } - if (HasExceededMaxRetries()) + if (!stream_->GetAlternativeService(&retried_alternative_service_)) { + // If there was no alternative service used for this request, then there + // is no alternative service to be disabled. Note: We expect this + // doesn't happen. But records the UMA just in case. + RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus::kNoRetryNoAlternativeService); break; + } + if (HasExceededMaxRetries()) { + RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus::kNoRetryExceededMaxRetries); + break; + } + if (session_->http_server_properties()->IsAlternativeServiceBroken( retried_alternative_service_, network_anonymization_key_)) { // If the alternative service was marked as broken while the request // was in flight, retry the request which will not use the broken // alternative service. + RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus::kRetryAltServiceBroken); net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); retry_attempts_++; - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(*retry_reason); error = OK; } else if (session_->context() .quic_context->params() @@ -1669,14 +1744,24 @@ int HttpNetworkTransaction::HandleIOError(int error) { // Disable alternative services for this request and retry it. If the // retry succeeds, then the alternative service will be marked as // broken then. + RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus::kRetryAltServiceNotBroken); enable_alternative_services_ = false; net_log_.AddEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); retry_attempts_++; - ResetConnectionAndRequestForResend(); + ResetConnectionAndRequestForResend(*retry_reason); error = OK; } break; + + // The following reasons are not covered here. + case RetryReason::kHttpRequestTimeout: + case RetryReason::kHttpMisdirectedRequest: + case RetryReason::kHttp11Required: + case RetryReason::kSslClientAuthSignatureFailed: + NOTREACHED(); + break; } return error; } @@ -1707,8 +1792,8 @@ void HttpNetworkTransaction::ResetStateForAuthRestart() { net_error_details_.quic_connection_error = quic::QUIC_NO_ERROR; #if BUILDFLAG(ENABLE_REPORTING) network_error_logging_report_generated_ = false; - start_timeticks_ = base::TimeTicks::Now(); #endif // BUILDFLAG(ENABLE_REPORTING) + start_timeticks_ = base::TimeTicks::Now(); } void HttpNetworkTransaction::CacheNetErrorDetailsAndResetStream() { @@ -1740,7 +1825,18 @@ bool HttpNetworkTransaction::CheckMaxRestarts() { return num_restarts_ < kMaxRestarts; } -void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { +void HttpNetworkTransaction::ResetConnectionAndRequestForResend( + RetryReason retry_reason) { + base::UmaHistogramEnumeration( + IsGoogleHostWithAlpnH3(url_.host()) + ? "Net.NetworkTransactionH3SupportedGoogleHost.RetryReason" + : "Net.NetworkTransaction.RetryReason", + retry_reason); + if (retry_reason == RetryReason::kQuicProtocolError) { + quic_protocol_error_retry_delay_ = + base::TimeTicks::Now() - start_timeticks_; + } + if (stream_.get()) { stream_->Close(true); CacheNetErrorDetailsAndResetStream(); @@ -1755,8 +1851,8 @@ void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { #if BUILDFLAG(ENABLE_REPORTING) // Reset for new request. network_error_logging_report_generated_ = false; - start_timeticks_ = base::TimeTicks::Now(); #endif // BUILDFLAG(ENABLE_REPORTING) + start_timeticks_ = base::TimeTicks::Now(); ResetStateForRestart(); } @@ -1888,4 +1984,43 @@ bool HttpNetworkTransaction::ContentEncodingsValid() const { return result; } +void HttpNetworkTransaction::RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus retry_status) { + std::string histogram = "Net.QuicProtocolError"; + if (IsGoogleHostWithAlpnH3(url_.host())) { + histogram += "H3SupportedGoogleHost"; + } + base::UmaHistogramEnumeration(histogram + ".RetryStatus", retry_status); + + if (!stream_) { + return; + } + absl::optional<quic::QuicErrorCode> connection_error = + stream_->GetQuicErrorCode(); + absl::optional<quic::QuicRstStreamErrorCode> stream_error = + stream_->GetQuicRstStreamErrorCode(); + if (!connection_error || !stream_error) { + return; + } + switch (retry_status) { + case QuicProtocolErrorRetryStatus::kNoRetryExceededMaxRetries: + histogram += ".NoRetryExceededMaxRetries"; + break; + case QuicProtocolErrorRetryStatus::kNoRetryHeaderReceived: + histogram += ".NoRetryHeaderReceived"; + break; + case QuicProtocolErrorRetryStatus::kNoRetryNoAlternativeService: + histogram += ".NoRetryNoAlternativeService"; + break; + case QuicProtocolErrorRetryStatus::kRetryAltServiceBroken: + histogram += ".RetryAltServiceBroken"; + break; + case QuicProtocolErrorRetryStatus::kRetryAltServiceNotBroken: + histogram += ".RetryAltServiceNotBroken"; + break; + } + base::UmaHistogramSparse(histogram + ".QuicErrorCode", *connection_error); + base::UmaHistogramSparse(histogram + ".QuicStreamErrorCode", *stream_error); +} + } // namespace net diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 4ef51e66b..8e6ade134 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -34,6 +34,7 @@ #include "net/socket/connection_attempts.h" #include "net/ssl/ssl_config_service.h" #include "net/websockets/websocket_handshake_stream_base.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace net { @@ -261,9 +262,35 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction // proceed. bool CheckMaxRestarts(); + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class RetryReason { + kHttpRequestTimeout = 0, + kHttpMisdirectedRequest = 1, + kHttp11Required = 2, + kSslClientAuthSignatureFailed = 3, + kConnectionReset = 4, + kConnectionClosed = 5, + kConnectionAborted = 6, + kSocketNotConnected = 7, + kEmptyResponse = 8, + kEarlyDataRejected = 9, + kWrongVersionOnEarlyData = 10, + kHttp2PingFailed = 11, + kHttp2ServerRefusedStream = 12, + kHttp2PushedStreamNotAvailable = 13, + kHttp2ClaimedPushedStreamResetByServer = 14, + kHttp2PushedResponseDoesNotMatch = 15, + kQuicHandshakeFailed = 16, + kQuicGoawayRequestCanBeRetried = 17, + kQuicProtocolError = 18, + kMaxValue = kQuicProtocolError, + }; + static absl::optional<RetryReason> GetRetryReasonForIOError(int error); + // Resets the connection and the request headers for resend. Called when // ShouldResendRequest() is true. - void ResetConnectionAndRequestForResend(); + void ResetConnectionAndRequestForResend(RetryReason retry_reason); // Sets up the state machine to restart the transaction with auth. void PrepareForAuthRestart(HttpAuth::Target target); @@ -312,6 +339,23 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction void ResumeAfterConnected(int result); + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class QuicProtocolErrorRetryStatus { + kNoRetryExceededMaxRetries = 0, + kNoRetryHeaderReceived = 1, + kNoRetryNoAlternativeService = 2, + kRetryAltServiceBroken = 3, + kRetryAltServiceNotBroken = 4, + kMaxValue = kRetryAltServiceNotBroken, + }; + + void RecordQuicProtocolErrorMetrics( + QuicProtocolErrorRetryStatus retry_status); + + void RecordMetricsIfError(int rv); + void RecordMetrics(int rv); + scoped_refptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS]; @@ -376,8 +420,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction std::string request_referrer_; std::string request_user_agent_; int request_reporting_upload_depth_ = 0; - base::TimeTicks start_timeticks_; #endif + base::TimeTicks start_timeticks_; // The size in bytes of the buffer we use to drain the response body that // we want to throw away. The response body is typically a small error @@ -449,6 +493,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction size_t num_restarts_ = 0; bool close_connection_on_destruction_ = false; + + absl::optional<base::TimeDelta> quic_protocol_error_retry_delay_; }; } // namespace net diff --git a/net/http/http_status_code_list.h b/net/http/http_status_code_list.h index 49ccb19bd..b954fbe04 100644 --- a/net/http/http_status_code_list.h +++ b/net/http/http_status_code_list.h @@ -70,6 +70,7 @@ HTTP_STATUS_ENUM_VALUE(REQUESTED_RANGE_NOT_SATISFIABLE, HTTP_STATUS_ENUM_VALUE(EXPECTATION_FAILED, 417, "Expectation Failed") // 418 returned by Cloud Print. HTTP_STATUS_ENUM_VALUE(INVALID_XPRIVET_TOKEN, 418, "Invalid XPrivet Token") +HTTP_STATUS_ENUM_VALUE(UNPROCESSABLE_CONTENT, 422, "Unprocessable Content") HTTP_STATUS_ENUM_VALUE(TOO_EARLY, 425, "Too Early") HTTP_STATUS_ENUM_VALUE(TOO_MANY_REQUESTS, 429, "Too Many Requests") diff --git a/net/http/http_stream.cc b/net/http/http_stream.cc new file mode 100644 index 000000000..d1e6acec5 --- /dev/null +++ b/net/http/http_stream.cc @@ -0,0 +1,18 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_stream.h" + +namespace net { + +absl::optional<quic::QuicErrorCode> HttpStream::GetQuicErrorCode() const { + return absl::nullopt; +} + +absl::optional<quic::QuicRstStreamErrorCode> +HttpStream::GetQuicRstStreamErrorCode() const { + return absl::nullopt; +} + +} // namespace net diff --git a/net/http/http_stream.h b/net/http/http_stream.h index f8f40d216..7e373920e 100644 --- a/net/http/http_stream.h +++ b/net/http/http_stream.h @@ -24,6 +24,8 @@ #include "net/base/net_export.h" #include "net/base/request_priority.h" #include "net/http/http_raw_request_headers.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_error_codes.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace net { @@ -211,6 +213,15 @@ class NET_EXPORT_PRIVATE HttpStream { // Accept-CH header fields received in HTTP responses, this value is available // before any requests are made. virtual base::StringPiece GetAcceptChViaAlps() const = 0; + + // If `this` is using a Quic stream, set the `connection_error` of the Quic + // stream. Otherwise returns nullopt. + virtual absl::optional<quic::QuicErrorCode> GetQuicErrorCode() const; + + // If `this` is using a Quic stream, set the `stream_error' status of the Quic + // stream. Otherwise returns nullopt. + virtual absl::optional<quic::QuicRstStreamErrorCode> + GetQuicRstStreamErrorCode() const; }; } // namespace net diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins index 5f3c3a3fe..e39adae5c 100644 --- a/net/http/transport_security_state_static.pins +++ b/net/http/transport_security_state_static.pins @@ -43,9 +43,9 @@ # hash function for preloaded entries again (we have already done so once). # -# Last updated: 2023-04-10 12:55 UTC +# Last updated: 2023-04-13 12:54 UTC PinsListTimestamp -1681131302 +1681390459 TestSPKI sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h index 36ed21a38..c35221a45 100644 --- a/net/log/net_log_event_type_list.h +++ b/net/log/net_log_event_type_list.h @@ -4306,18 +4306,27 @@ EVENT_TYPE(TRANSPORT_SECURITY_STATE_SHOULD_UPGRADE_TO_SSL) // Oblivious HTTP // ------------------------------------------------------------------------ -// OBLIVIOUS_HTTP_REQUEST_START is emitted when an oblivious HTTP request is -// started. -EVENT_TYPE(OBLIVIOUS_HTTP_REQUEST_START) - -// OBLIVIOUS_HTTP_REQUEST_END is emitted when an oblivious HTTP request ends. -// The net error "status" code for the request is attached. -EVENT_TYPE(OBLIVIOUS_HTTP_REQUEST_END) +// OBLIVIOUS_HTTP_REQUEST measures the time between when Oblivoius HTTP +// has begun and when Oblivious HTTP has ended (either success or failure). +// The following parameters are attached: +// { +// "net_error": <The net error code integer, can be a failure or a success>, +// "outer_response_error_code": <The HTTP error code of the outer relay HTTP +// response, set iff the result fails because the outer HTTP response status +// code is not HTTP_OK>, +// "inner_response_code": <The HTTP code of the inner gateway HTTP response, +// parsed out from the binary HTTP structure> +// } +EVENT_TYPE(OBLIVIOUS_HTTP_REQUEST) -// OBLIVIOUS_HTTP_REQUEST_DATA logs either just the headers of the request or +// OBLIVIOUS_HTTP_REQUEST_DATA logs either just the byte count of the request or // the entire request (depending on capture settings), before encryption. EVENT_TYPE(OBLIVIOUS_HTTP_REQUEST_DATA) -// OBLIVIOUS_HTTP_RESPONSE_DATA logs either just the headers of the response or -// the entire response (depending on capture settings), after decryption. +// OBLIVIOUS_HTTP_RESPONSE_DATA logs either just the byte count of the response +// or the entire response (depending on capture settings), after decryption. EVENT_TYPE(OBLIVIOUS_HTTP_RESPONSE_DATA) + +// OBLIVIOUS_HTTP_RESPONSE_HEADERS logs headers of the response, after +// decryption. +EVENT_TYPE(OBLIVIOUS_HTTP_RESPONSE_HEADERS) diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc index 27b10c6cb..8d9ade2af 100644 --- a/net/nqe/network_quality_estimator.cc +++ b/net/nqe/network_quality_estimator.cc @@ -213,16 +213,9 @@ void NetworkQualityEstimator::NotifyStartTransaction( if (!RequestSchemeIsHTTPOrHTTPS(request)) return; - // Update |estimated_quality_at_last_main_frame_| if this is a main frame - // request. // TODO(tbansal): Refactor this to a separate method. if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) { - base::TimeTicks now = tick_clock_->NowTicks(); - last_main_frame_request_ = now; - ComputeEffectiveConnectionType(); - effective_connection_type_at_last_main_frame_ = effective_connection_type_; - estimated_quality_at_last_main_frame_ = network_quality_; } else { MaybeComputeEffectiveConnectionType(); } @@ -293,7 +286,6 @@ void NetworkQualityEstimator::NotifyHeadersReceived( if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) { ComputeEffectiveConnectionType(); - RecordMetricsOnMainFrameRequest(); } LoadTimingInfo load_timing_info; @@ -477,8 +469,6 @@ void NetworkQualityEstimator::OnConnectionTypeChanged( network_quality_ = nqe::internal::NetworkQuality(); end_to_end_rtt_ = absl::nullopt; effective_connection_type_ = EFFECTIVE_CONNECTION_TYPE_UNKNOWN; - effective_connection_type_at_last_main_frame_ = - EFFECTIVE_CONNECTION_TYPE_UNKNOWN; rtt_observations_size_at_last_ect_computation_ = 0; throughput_observations_size_at_last_ect_computation_ = 0; new_rtt_observations_since_last_ect_computation_ = 0; @@ -486,7 +476,6 @@ void NetworkQualityEstimator::OnConnectionTypeChanged( transport_rtt_observation_count_last_ect_computation_ = 0; end_to_end_rtt_observation_count_at_last_ect_computation_ = 0; last_socket_watcher_rtt_notification_ = base::TimeTicks(); - estimated_quality_at_last_main_frame_ = nqe::internal::NetworkQuality(); cached_estimate_applied_ = false; GatherEstimatesForNextConnectionType(); @@ -527,7 +516,6 @@ void NetworkQualityEstimator::ContinueGatherEstimatesForNextConnectionType( DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Update the local state as part of preparation for the new connection. current_network_id_ = network_id; - RecordNetworkIDAvailability(); // Read any cached estimates for the new network. If cached estimates are // unavailable, add the default estimates. @@ -537,47 +525,6 @@ void NetworkQualityEstimator::ContinueGatherEstimatesForNextConnectionType( ComputeEffectiveConnectionType(); } -void NetworkQualityEstimator::RecordNetworkIDAvailability() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (current_network_id_.type == - NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI || - NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { - UMA_HISTOGRAM_BOOLEAN("NQE.NetworkIdAvailable", - !current_network_id_.id.empty()); - } -} - -void NetworkQualityEstimator::RecordMetricsOnMainFrameRequest() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (estimated_quality_at_last_main_frame_.http_rtt() != - nqe::internal::InvalidRTT()) { - // Add the 50th percentile value. - LOCAL_HISTOGRAM_TIMES("NQE.MainFrame.RTT.Percentile50", - estimated_quality_at_last_main_frame_.http_rtt()); - } - - if (estimated_quality_at_last_main_frame_.transport_rtt() != - nqe::internal::InvalidRTT()) { - // Add the 50th percentile value. - LOCAL_HISTOGRAM_TIMES( - "NQE.MainFrame.TransportRTT.Percentile50", - estimated_quality_at_last_main_frame_.transport_rtt()); - } - - if (estimated_quality_at_last_main_frame_.downstream_throughput_kbps() != - nqe::internal::INVALID_RTT_THROUGHPUT) { - // Add the 50th percentile value. - LOCAL_HISTOGRAM_COUNTS_1000000( - "NQE.MainFrame.Kbps.Percentile50", - estimated_quality_at_last_main_frame_.downstream_throughput_kbps()); - } - - LOCAL_HISTOGRAM_ENUMERATION("NQE.MainFrame.EffectiveConnectionType", - effective_connection_type_at_last_main_frame_, - EFFECTIVE_CONNECTION_TYPE_LAST); -} - void NetworkQualityEstimator::ComputeEffectiveConnectionType() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -599,31 +546,14 @@ void NetworkQualityEstimator::ComputeEffectiveConnectionType() { network_quality_ = nqe::internal::NetworkQuality(http_rtt, transport_rtt, downstream_throughput_kbps); ClampKbpsBasedOnEct(); - - UMA_HISTOGRAM_ENUMERATION("NQE.EffectiveConnectionType.OnECTComputation", - effective_connection_type_, - EFFECTIVE_CONNECTION_TYPE_LAST); if (network_quality_.http_rtt() != nqe::internal::InvalidRTT()) { UMA_HISTOGRAM_TIMES("NQE.RTT.OnECTComputation", network_quality_.http_rtt()); } - if (network_quality_.transport_rtt() != nqe::internal::InvalidRTT()) { - UMA_HISTOGRAM_TIMES("NQE.TransportRTT.OnECTComputation", - network_quality_.transport_rtt()); - } - - if (end_to_end_rtt != nqe::internal::InvalidRTT()) { - UMA_HISTOGRAM_TIMES("NQE.EndToEndRTT.OnECTComputation", end_to_end_rtt); - } end_to_end_rtt_ = absl::nullopt; - if (end_to_end_rtt != nqe::internal::InvalidRTT()) + if (end_to_end_rtt != nqe::internal::InvalidRTT()) { end_to_end_rtt_ = end_to_end_rtt; - - if (network_quality_.downstream_throughput_kbps() != - nqe::internal::INVALID_RTT_THROUGHPUT) { - UMA_HISTOGRAM_COUNTS_1M("NQE.Kbps.OnECTComputation", - network_quality_.downstream_throughput_kbps()); } NotifyObserversOfRTTOrThroughputComputed(); @@ -690,8 +620,6 @@ void NetworkQualityEstimator::AdjustHttpRttBasedOnRTTCounts( params_->http_rtt_transport_rtt_min_count() || end_to_end_rtt_observation_count_at_last_ect_computation_ >= params_->http_rtt_transport_rtt_min_count()) { - UMA_HISTOGRAM_TIMES("NQE.HttpRttReduction.BasedOnRTTCounts", - base::TimeDelta()); return; } @@ -701,8 +629,6 @@ void NetworkQualityEstimator::AdjustHttpRttBasedOnRTTCounts( tick_clock_->NowTicks() - last_connection_change_; if (cached_estimate_applied_ && time_since_connection_change <= base::Minutes(1)) { - UMA_HISTOGRAM_TIMES("NQE.HttpRttReduction.BasedOnRTTCounts", - base::TimeDelta()); return; } @@ -711,8 +637,6 @@ void NetworkQualityEstimator::AdjustHttpRttBasedOnRTTCounts( // HTTP RTT can't be trusted due to hanging GETs. In that case, return the // typical HTTP RTT for a fast connection. if (current_network_id_.type == net::NetworkChangeNotifier::CONNECTION_NONE) { - UMA_HISTOGRAM_TIMES("NQE.HttpRttReduction.BasedOnRTTCounts", - base::TimeDelta()); return; } @@ -720,15 +644,10 @@ void NetworkQualityEstimator::AdjustHttpRttBasedOnRTTCounts( params_->TypicalNetworkQuality(net::EFFECTIVE_CONNECTION_TYPE_4G) .http_rtt(); if (upper_bound_http_rtt > *http_rtt) { - UMA_HISTOGRAM_TIMES("NQE.HttpRttReduction.BasedOnRTTCounts", - base::TimeDelta()); return; } DCHECK_LE(upper_bound_http_rtt, *http_rtt); - - UMA_HISTOGRAM_TIMES("NQE.HttpRttReduction.BasedOnRTTCounts", - *http_rtt - upper_bound_http_rtt); *http_rtt = upper_bound_http_rtt; } @@ -1036,11 +955,10 @@ bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { const bool cached_estimate_available = network_quality_store_->GetById( current_network_id_, &cached_network_quality); - UMA_HISTOGRAM_BOOLEAN("NQE.CachedNetworkQualityAvailable", - cached_estimate_available); - if (!cached_estimate_available) + if (!cached_estimate_available) { return false; + } EffectiveConnectionType effective_connection_type = cached_network_quality.effective_connection_type(); @@ -1195,10 +1113,6 @@ void NetworkQualityEstimator::AddAndNotifyObserversOfThroughput( ++new_throughput_observations_since_last_ect_computation_; http_downstream_throughput_kbps_observations_.AddObservation(observation); - LOCAL_HISTOGRAM_ENUMERATION("NQE.Kbps.ObservationSource", - observation.source(), - NETWORK_QUALITY_OBSERVATION_SOURCE_MAX); - // Maybe recompute the effective connection type since a new throughput // observation is available. if (observation.source() != @@ -1507,20 +1421,6 @@ void NetworkQualityEstimator::OnPeerToPeerConnectionsCountChange( if (p2p_connections_count_ == count) return; - if (p2p_connections_count_ == 0 && count > 0) { - DCHECK(!p2p_connections_count_active_timestamp_); - p2p_connections_count_active_timestamp_ = tick_clock_->NowTicks(); - } - - if (p2p_connections_count_ > 0 && count == 0) { - DCHECK(p2p_connections_count_active_timestamp_); - base::TimeDelta duration = tick_clock_->NowTicks() - - p2p_connections_count_active_timestamp_.value(); - LOCAL_HISTOGRAM_CUSTOM_TIMES("NQE.PeerToPeerConnectionsDuration", duration, - base::Milliseconds(1), base::Hours(1), 50); - p2p_connections_count_active_timestamp_ = absl::nullopt; - } - p2p_connections_count_ = count; for (auto& observer : peer_to_peer_type_observer_list_) { diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h index 6927a0535..6268c2e33 100644 --- a/net/nqe/network_quality_estimator.h +++ b/net/nqe/network_quality_estimator.h @@ -442,13 +442,6 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator // should discard RTT if it is set to the value returned by |InvalidRTT()|. static const base::TimeDelta InvalidRTT(); - // Records UMA on whether the NetworkID was available or not. Called right - // after a network change event. - void RecordNetworkIDAvailability() const; - - // Records UMA on main frame requests. - void RecordMetricsOnMainFrameRequest() const; - // Records a downstream throughput observation to the observation buffer if // a valid observation is available. |downstream_kbps| is the downstream // throughput in kilobits per second. @@ -532,11 +525,6 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator // type. void ClampKbpsBasedOnEct(); - // Earliest timestamp since when there is at least one active peer to peer - // connection count. Set to current timestamp when |p2p_connections_count_| - // changes from 0 to 1. Reset to null when |p2p_connections_count_| becomes 0. - absl::optional<base::TimeTicks> p2p_connections_count_active_timestamp_; - // Determines if the requests to local host can be used in estimating the // network quality. Set to true only for tests. bool use_localhost_requests_ = false; @@ -567,15 +555,6 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator ObservationBuffer rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_COUNT]; - // Time when the transaction for the last main frame request was started. - base::TimeTicks last_main_frame_request_; - - // Estimated network quality when the transaction for the last main frame - // request was started. - nqe::internal::NetworkQuality estimated_quality_at_last_main_frame_; - EffectiveConnectionType effective_connection_type_at_last_main_frame_ = - EFFECTIVE_CONNECTION_TYPE_UNKNOWN; - // Observer lists for round trip times and throughput measurements. base::ObserverList<RTTObserver>::Unchecked rtt_observer_list_; base::ObserverList<ThroughputObserver>::Unchecked throughput_observer_list_; diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc index 013bdb6bf..2b7948170 100644 --- a/net/nqe/network_quality_estimator_unittest.cc +++ b/net/nqe/network_quality_estimator_unittest.cc @@ -216,8 +216,6 @@ TEST_F(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) { estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); - histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", - false, 2); base::TimeDelta rtt; int32_t kbps; @@ -270,18 +268,11 @@ TEST_F(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) { "downstream_throughput_kbps")); // Check UMA histograms. - histogram_tester.ExpectUniqueSample( - "NQE.MainFrame.EffectiveConnectionType", - EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 1); EXPECT_LE(1u, histogram_tester.GetAllSamples("NQE.RTT.OnECTComputation").size()); - EXPECT_LE(1u, - histogram_tester.GetAllSamples("NQE.Kbps.OnECTComputation").size()); histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); - histogram_tester.ExpectBucketCount( - "NQE.Kbps.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); std::unique_ptr<URLRequest> request2( context->CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, @@ -289,12 +280,9 @@ TEST_F(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) { request2->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); request2->Start(); test_delegate.RunUntilComplete(); - histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", 2); estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); - histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", - false, 3); histogram_tester.ExpectTotalCount("NQE.RatioMedianRTT.WiFi", 0); EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, @@ -302,16 +290,8 @@ TEST_F(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) { EXPECT_FALSE( estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); - // Verify that metrics are logged correctly on main-frame requests. - histogram_tester.ExpectTotalCount("NQE.MainFrame.RTT.Percentile50", 1); - histogram_tester.ExpectTotalCount("NQE.MainFrame.TransportRTT.Percentile50", - 0); - histogram_tester.ExpectTotalCount("NQE.MainFrame.Kbps.Percentile50", 1); - estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, std::string()); - histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", - false, 4); EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, base::TimeTicks(), &rtt, nullptr)); @@ -324,15 +304,9 @@ TEST_F(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) { request3->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); request3->Start(); test_delegate.RunUntilComplete(); - histogram_tester.ExpectBucketCount( - "NQE.MainFrame.EffectiveConnectionType", - EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 2); - histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", 3); estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); - histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", false, - 4); } // Tests that the network quality estimator writes and reads network quality @@ -354,8 +328,6 @@ TEST_F(NetworkQualityEstimatorTest, Caching) { : ""; estimator.SimulateNetworkChange(connection_type, connection_id); - histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", - false, 2); base::TimeDelta rtt; int32_t kbps; @@ -402,9 +374,6 @@ TEST_F(NetworkQualityEstimatorTest, Caching) { base::TimeTicks(), &rtt, nullptr)); EXPECT_FALSE(estimator.GetTransportRTT()); - histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", - false, 2); - // Add the observers before changing the network type. TestEffectiveConnectionTypeObserver observer; estimator.AddEffectiveConnectionTypeObserver(&observer); @@ -430,10 +399,6 @@ TEST_F(NetworkQualityEstimatorTest, Caching) { NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 1); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 4); - histogram_tester.ExpectBucketCount( - "NQE.Kbps.ObservationSource", - NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE, 1); - // Verify the contents of the net log. EXPECT_LE( 1, estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED) - @@ -452,9 +417,6 @@ TEST_F(NetworkQualityEstimatorTest, Caching) { NetLogEventType::NETWORK_QUALITY_CHANGED, "effective_connection_type")); - histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", - true, 1); - histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 3); base::RunLoop().RunUntilIdle(); // Verify that the cached network quality was read, and observers were @@ -481,7 +443,6 @@ TEST_F(NetworkQualityEstimatorTest, CachingDisabled) { estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test"); - histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); base::TimeDelta rtt; int32_t kbps; @@ -524,8 +485,6 @@ TEST_F(NetworkQualityEstimatorTest, CachingDisabled) { base::TimeTicks(), &rtt, nullptr)); EXPECT_FALSE(estimator.GetTransportRTT()); - histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); - // Add the observers before changing the network type. TestRTTObserver rtt_observer; estimator.AddRTTObserver(&rtt_observer); @@ -535,7 +494,6 @@ TEST_F(NetworkQualityEstimatorTest, CachingDisabled) { estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test"); - histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); base::RunLoop().RunUntilIdle(); // Verify that the cached network quality was read, and observers were @@ -560,7 +518,6 @@ TEST_F(NetworkQualityEstimatorTest, QuicObservations) { NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, 1); histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC, 1); - histogram_tester.ExpectTotalCount("NQE.EndToEndRTT.OnECTComputation", 1); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 2); // Verify that the QUIC RTT samples are used when computing transport RTT @@ -582,7 +539,6 @@ TEST_F(NetworkQualityEstimatorTest, absl::nullopt); histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC, 1); - histogram_tester.ExpectTotalCount("NQE.EndToEndRTT.OnECTComputation", 1); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 1); EXPECT_EQ(base::Milliseconds(10), estimator.GetTransportRTT()); @@ -603,7 +559,6 @@ TEST_F(NetworkQualityEstimatorTest, histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_H2_PINGS, 1); - histogram_tester.ExpectTotalCount("NQE.EndToEndRTT.OnECTComputation", 1); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 1); EXPECT_EQ(base::Milliseconds(10), estimator.GetTransportRTT()); @@ -731,11 +686,7 @@ TEST_F(NetworkQualityEstimatorTest, DefaultObservations) { histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM, 1); - histogram_tester.ExpectBucketCount( - "NQE.Kbps.ObservationSource", - NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 1); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 2); - histogram_tester.ExpectTotalCount("NQE.Kbps.ObservationSource", 1); // Default observations should be added on connection change. estimator.SimulateNetworkChange( @@ -746,11 +697,7 @@ TEST_F(NetworkQualityEstimatorTest, DefaultObservations) { histogram_tester.ExpectBucketCount( "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM, 2); - histogram_tester.ExpectBucketCount( - "NQE.Kbps.ObservationSource", - NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 2); histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 4); - histogram_tester.ExpectTotalCount("NQE.Kbps.ObservationSource", 2); base::TimeDelta rtt; int32_t kbps; @@ -1394,9 +1341,6 @@ TEST_F(NetworkQualityEstimatorTest, MAYBE_TestEffectiveConnectionTypeObserver) { NetLogEventType::NETWORK_QUALITY_CHANGED, "downstream_throughput_kbps")); - histogram_tester.ExpectUniqueSample("NQE.MainFrame.EffectiveConnectionType", - EFFECTIVE_CONNECTION_TYPE_2G, 1); - // Next request should not trigger recomputation of effective connection type // since there has been no change in the clock. std::unique_ptr<URLRequest> request2( @@ -1765,12 +1709,6 @@ TEST_F(NetworkQualityEstimatorTest, request->Start(); test_delegate.RunUntilComplete(); EXPECT_EQ(1U, observer.effective_connection_types().size()); - histogram_tester.ExpectUniqueSample("NQE.MainFrame.EffectiveConnectionType", - EFFECTIVE_CONNECTION_TYPE_2G, 1); - EXPECT_LE(1u, - histogram_tester - .GetAllSamples("NQE.EffectiveConnectionType.OnECTComputation") - .size()); size_t expected_effective_connection_type_notifications = 1; EXPECT_EQ(expected_effective_connection_type_notifications, @@ -1932,11 +1870,6 @@ TEST_F(NetworkQualityEstimatorTest, TestRttThroughputObservers) { EXPECT_EQ(quic_rtt, estimator.end_to_end_rtt_.value()); EXPECT_LT( 0u, estimator.end_to_end_rtt_observation_count_at_last_ect_computation_); - const std::vector<base::Bucket> end_to_end_rtt_samples = - histogram_tester.GetAllSamples("NQE.EndToEndRTT.OnECTComputation"); - EXPECT_FALSE(end_to_end_rtt_samples.empty()); - for (const auto& bucket : end_to_end_rtt_samples) - EXPECT_EQ(quic_rtt.InMilliseconds(), bucket.min); } TEST_F(NetworkQualityEstimatorTest, TestGlobalSocketWatcherThrottle) { @@ -2087,32 +2020,11 @@ TEST_F(NetworkQualityEstimatorTest, MAYBE_TestTCPSocketRTT) { estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); - // Verify that metrics are logged correctly on main-frame requests. - histogram_tester.ExpectTotalCount("NQE.MainFrame.TransportRTT.Percentile50", - num_requests); - - histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", - num_requests); - histogram_tester.ExpectBucketCount("NQE.MainFrame.EffectiveConnectionType", - EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 0); ExpectBucketCountAtLeast(&histogram_tester, "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, 1); - ExpectBucketCountAtLeast(&histogram_tester, "NQE.Kbps.ObservationSource", - NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); - EXPECT_LE(1u, - histogram_tester - .GetAllSamples("NQE.EffectiveConnectionType.OnECTComputation") - .size()); - EXPECT_LE(1u, - histogram_tester.GetAllSamples("NQE.TransportRTT.OnECTComputation") - .size()); EXPECT_LE(1u, histogram_tester.GetAllSamples("NQE.RTT.OnECTComputation").size()); - histogram_tester.ExpectBucketCount( - "NQE.Kbps.ObservationSource", - NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 0); - estimator.SimulateNetworkChange( NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test"); histogram_tester.ExpectBucketCount( @@ -2126,44 +2038,6 @@ TEST_F(NetworkQualityEstimatorTest, MAYBE_TestTCPSocketRTT) { NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 2); } -TEST_F(NetworkQualityEstimatorTest, TestRecordNetworkIDAvailability) { - TestNetworkQualityEstimator estimator; - - // Create the histogram tester after |estimator| is constructed. This ensures - // that any network checks done at the time of |estimator| construction do not - // affect |histogram_tester|. - base::HistogramTester histogram_tester; - - // The NetworkID is recorded as available on Wi-Fi connection. - estimator.SimulateNetworkChange( - NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); - histogram_tester.ExpectUniqueSample("NQE.NetworkIdAvailable", 1, 1); - - // The histogram is not recorded on an unknown connection. - estimator.SimulateNetworkChange( - NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, ""); - histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 1); - - // The NetworkID is recorded as not being available on a Wi-Fi connection - // with an empty SSID. - estimator.SimulateNetworkChange( - NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, ""); - histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 0, 1); - histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 2); - - // The NetworkID is recorded as being available on a Wi-Fi connection. - estimator.SimulateNetworkChange( - NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); - histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 1, 2); - histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 3); - - // The NetworkID is recorded as being available on a cellular connection. - estimator.SimulateNetworkChange( - NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1"); - histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 1, 3); - histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 4); -} - class TestNetworkQualitiesCacheObserver : public nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver { public: @@ -2893,29 +2767,6 @@ TEST_F(NetworkQualityEstimatorTest, HangingRequestUsingTransportAndHttpOnly) { } } -TEST_F(NetworkQualityEstimatorTest, PeerToPeerConnectionCounts) { - base::SimpleTestTickClock tick_clock; - TestNetworkQualityEstimator estimator; - estimator.SetTickClockForTesting(&tick_clock); - estimator.OnPeerToPeerConnectionsCountChange(3u); - - base::TimeDelta advance_1 = base::Minutes(4); - tick_clock.Advance(advance_1); - - base::HistogramTester histogram_tester; - histogram_tester.ExpectTotalCount("NQE.PeerToPeerConnectionsDuration", 0); - - estimator.OnPeerToPeerConnectionsCountChange(1u); - base::TimeDelta advance_2 = base::Minutes(6); - tick_clock.Advance(advance_2); - histogram_tester.ExpectTotalCount("NQE.PeerToPeerConnectionsDuration", 0); - - estimator.OnPeerToPeerConnectionsCountChange(0u); - histogram_tester.ExpectUniqueSample("NQE.PeerToPeerConnectionsDuration", - (advance_1 + advance_2).InMilliseconds(), - 1); -} - TEST_F(NetworkQualityEstimatorTest, TestPeerToPeerConnectionsCountObserver) { TestPeerToPeerConnectionsCountObserver observer; TestNetworkQualityEstimator estimator; diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc index c646c5d2f..c6973e29c 100644 --- a/net/quic/quic_http_stream.cc +++ b/net/quic/quic_http_stream.cc @@ -433,6 +433,21 @@ base::StringPiece QuicHttpStream::GetAcceptChViaAlps() const { return session()->GetAcceptChViaAlps(url::SchemeHostPort(request_info_->url)); } +absl::optional<quic::QuicErrorCode> QuicHttpStream::GetQuicErrorCode() const { + if (stream_) { + return stream_->connection_error(); + } + return connection_error_; +} + +absl::optional<quic::QuicRstStreamErrorCode> +QuicHttpStream::GetQuicRstStreamErrorCode() const { + if (stream_) { + return stream_->stream_error(); + } + return stream_error_; +} + void QuicHttpStream::ReadTrailingHeaders() { int rv = stream_->ReadTrailingHeaders( &trailing_header_block_, @@ -762,6 +777,8 @@ void QuicHttpStream::ResetStream() { closed_stream_received_bytes_ = stream_->NumBytesConsumed(); closed_stream_sent_bytes_ = stream_->stream_bytes_written(); closed_is_first_stream_ = stream_->IsFirstStream(); + connection_error_ = stream_->connection_error(); + stream_error_ = stream_->stream_error(); } int QuicHttpStream::MapStreamError(int rv) { diff --git a/net/quic/quic_http_stream.h b/net/quic/quic_http_stream.h index 812bc4e24..5b6872efd 100644 --- a/net/quic/quic_http_stream.h +++ b/net/quic/quic_http_stream.h @@ -76,6 +76,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream : public MultiplexedHttpStream { void SetRequestIdempotency(Idempotency idempotency) override; const std::set<std::string>& GetDnsAliases() const override; base::StringPiece GetAcceptChViaAlps() const override; + absl::optional<quic::QuicErrorCode> GetQuicErrorCode() const override; + absl::optional<quic::QuicRstStreamErrorCode> GetQuicRstStreamErrorCode() + const override; static HttpResponseInfo::ConnectionInfo ConnectionInfoFromQuicVersion( quic::ParsedQuicVersion quic_version); @@ -205,6 +208,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream : public MultiplexedHttpStream { // the default value of false. bool closed_is_first_stream_ = false; + absl::optional<quic::QuicErrorCode> connection_error_; + absl::optional<quic::QuicRstStreamErrorCode> stream_error_; + // The caller's callback to be used for asynchronous operations. CompletionOnceCallback callback_; diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc index 4989bf000..0f1192063 100644 --- a/net/socket/ssl_client_socket_impl.cc +++ b/net/socket/ssl_client_socket_impl.cc @@ -238,18 +238,6 @@ RSAKeyUsage CheckRSAKeyUsage(const X509Certificate* cert, : RSAKeyUsage::kMissingDigitalSignature; } -// IsCECPQ2Host returns true if the given host is eligible for CECPQ2. This is -// used to implement a gradual rollout as the larger TLS messages may cause -// middlebox issues. -bool IsCECPQ2Host(const std::string& host) { - // Currently only eTLD+1s that start with "aa" are included, for example - // aardvark.com or aaron.com. - return registry_controlled_domains::GetDomainAndRegistry( - host, registry_controlled_domains::PrivateRegistryFilter:: - EXCLUDE_PRIVATE_REGISTRIES) - .find(features::kPostQuantumCECPQ2Prefix.Get()) == 0; -} - bool HostIsIPAddressNoBrackets(base::StringPiece host) { // Note this cannot directly call url::HostIsIPAddress, because that function // expects bracketed IPv6 literals. By the time hosts reach SSLClientSocket, @@ -758,20 +746,10 @@ int SSLClientSocketImpl::Init() { return ERR_UNEXPECTED; } - if (base::FeatureList::IsEnabled(features::kPostQuantumKyber)) { + if (context_->config().post_quantum_enabled && + base::FeatureList::IsEnabled(features::kPostQuantumKyber)) { static const int kCurves[] = {NID_X25519Kyber768, NID_X25519, - NID_P256Kyber768, NID_X9_62_prime256v1, - NID_secp384r1}; - if (!SSL_set1_curves(ssl_.get(), kCurves, std::size(kCurves))) { - return ERR_UNEXPECTED; - } - } else if (context_->config().cecpq2_enabled && - (base::FeatureList::IsEnabled(features::kPostQuantumCECPQ2) || - (!host_is_ip_address && - base::FeatureList::IsEnabled(features::kPostQuantumCECPQ2SomeDomains) && - IsCECPQ2Host(host_and_port_.host())))) { - static const int kCurves[] = {NID_CECPQ2, NID_X25519, NID_X9_62_prime256v1, - NID_secp384r1}; + NID_X9_62_prime256v1, NID_secp384r1}; if (!SSL_set1_curves(ssl_.get(), kCurves, std::size(kCurves))) { return ERR_UNEXPECTED; } diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc index ebdb5129f..d7fe60ca6 100644 --- a/net/socket/udp_socket_posix.cc +++ b/net/socket/udp_socket_posix.cc @@ -1063,6 +1063,14 @@ int UDPSocketPosix::SetDiffServCodePoint(DiffServCodePoint dscp) { return OK; } +int UDPSocketPosix::SetIPv6Only(bool ipv6_only) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (is_connected()) { + return ERR_SOCKET_IS_CONNECTED; + } + return net::SetIPv6Only(socket_, ipv6_only); +} + void UDPSocketPosix::DetachFromThread() { DETACH_FROM_THREAD(thread_checker_); } diff --git a/net/socket/udp_socket_posix.h b/net/socket/udp_socket_posix.h index 59eb51499..ed9c7549c 100644 --- a/net/socket/udp_socket_posix.h +++ b/net/socket/udp_socket_posix.h @@ -251,6 +251,10 @@ class NET_EXPORT UDPSocketPosix { // Returns a net error code. int SetDiffServCodePoint(DiffServCodePoint dscp); + // Sets IPV6_V6ONLY on the socket. If this flag is true, the socket will be + // restricted to only IPv6; false allows both IPv4 and IPv6 traffic. + int SetIPv6Only(bool ipv6_only); + // Exposes the underlying socket descriptor for testing its state. Does not // release ownership of the descriptor. SocketDescriptor SocketDescriptorForTesting() const { return socket_; } diff --git a/net/socket/udp_socket_win.cc b/net/socket/udp_socket_win.cc index 3e52f8c30..dfc234f5d 100644 --- a/net/socket/udp_socket_win.cc +++ b/net/socket/udp_socket_win.cc @@ -1185,6 +1185,14 @@ int UDPSocketWin::SetDiffServCodePoint(DiffServCodePoint dscp) { return OK; } +int UDPSocketWin::SetIPv6Only(bool ipv6_only) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (is_connected()) { + return ERR_SOCKET_IS_CONNECTED; + } + return net::SetIPv6Only(socket_, ipv6_only); +} + void UDPSocketWin::DetachFromThread() { DETACH_FROM_THREAD(thread_checker_); } diff --git a/net/socket/udp_socket_win.h b/net/socket/udp_socket_win.h index 421de9df4..857613564 100644 --- a/net/socket/udp_socket_win.h +++ b/net/socket/udp_socket_win.h @@ -334,11 +334,15 @@ class NET_EXPORT UDPSocketWin : public base::win::ObjectWatcher::Delegate { int SetMulticastLoopbackMode(bool loopback); // Sets the differentiated services flags on outgoing packets. May not do - // anything on some platforms. A return value of ERR_INVALID_HANDLE indicates + // anything on some platforms. A return value of ERR_INVALID_HANDLE indicates // the value was not set but could succeed on a future call, because // initialization is in progress. int SetDiffServCodePoint(DiffServCodePoint dscp); + // Sets IPV6_V6ONLY on the socket. If this flag is true, the socket will be + // restricted to only IPv6; false allows both IPv4 and IPv6 traffic. + int SetIPv6Only(bool ipv6_only); + // Resets the thread to be used for thread-safety checks. void DetachFromThread(); diff --git a/net/ssl/ssl_config_service.cc b/net/ssl/ssl_config_service.cc index 1570edecf..0bb171c02 100644 --- a/net/ssl/ssl_config_service.cc +++ b/net/ssl/ssl_config_service.cc @@ -19,11 +19,11 @@ namespace { bool SSLContextConfigsAreEqual(const net::SSLContextConfig& config1, const net::SSLContextConfig& config2) { return std::tie(config1.version_min, config1.version_max, - config1.disabled_cipher_suites, config1.cecpq2_enabled, - config1.ech_enabled, config1.insecure_hash_enabled) == + config1.disabled_cipher_suites, config1.post_quantum_enabled, + config1.ech_enabled, config1.insecure_hash_override) == std::tie(config2.version_min, config2.version_max, - config2.disabled_cipher_suites, config2.cecpq2_enabled, - config2.ech_enabled, config2.insecure_hash_enabled); + config2.disabled_cipher_suites, config2.post_quantum_enabled, + config2.ech_enabled, config2.insecure_hash_override); } } // namespace @@ -42,15 +42,8 @@ bool SSLContextConfig::EncryptedClientHelloEnabled() const { } bool SSLContextConfig::InsecureHashesInTLSHandshakesEnabled() const { - switch (insecure_hash_enabled) { - case insecure_hash_enabled_value::kUnset: - return base::FeatureList::IsEnabled(features::kSHA1ServerSignature); - case insecure_hash_enabled_value::kEnabled: - return true; - case insecure_hash_enabled_value::kDisabled: - return false; - } - NOTREACHED(); + return insecure_hash_override.value_or( + base::FeatureList::IsEnabled(features::kSHA1ServerSignature)); } SSLConfigService::SSLConfigService() diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h index 489cb7a34..e7c229d87 100644 --- a/net/ssl/ssl_config_service.h +++ b/net/ssl/ssl_config_service.h @@ -10,6 +10,7 @@ #include "base/observer_list.h" #include "net/base/net_export.h" #include "net/ssl/ssl_config.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace net { @@ -45,23 +46,16 @@ struct NET_EXPORT SSLContextConfig { std::vector<uint16_t> disabled_cipher_suites; // If false, disables post-quantum key agreement in TLS connections. - bool cecpq2_enabled = true; + bool post_quantum_enabled = true; // If false, disables TLS Encrypted ClientHello (ECH). If true, the feature // may be enabled or disabled, depending on feature flags. If querying whether // ECH is enabled, use `EncryptedClientHelloEnabled` instead. bool ech_enabled = true; - // If kEnabled, allows insecure hashes in TLS handshakes. If kDisabled, - // disallows insecure hashes in TLS handshakes. If kUnset use hashes - // determined by feature flags. - enum class insecure_hash_enabled_value { - kUnset, - kEnabled, - kDisabled, - }; - insecure_hash_enabled_value insecure_hash_enabled = - insecure_hash_enabled_value::kUnset; + // If specified, controls whether insecure hashes are allowed in TLS + // handshakes. If `absl::nullopt`, this is determined by feature flags. + absl::optional<bool> insecure_hash_override; // ADDING MORE HERE? Don't forget to update `SSLContextConfigsAreEqual`. }; diff --git a/net/test/embedded_test_server/http_request.cc b/net/test/embedded_test_server/http_request.cc index 4704ab10a..66d7569d1 100644 --- a/net/test/embedded_test_server/http_request.cc +++ b/net/test/embedded_test_server/http_request.cc @@ -171,7 +171,8 @@ HttpRequestParser::ParseResult HttpRequestParser::ParseHeaders() { LOG(WARNING) << "Malformed Content-Length header's value."; } } else if (http_request_->headers.count("Transfer-Encoding") > 0) { - if (http_request_->headers["Transfer-Encoding"] == "chunked") { + if (base::CompareCaseInsensitiveASCII( + http_request_->headers["Transfer-Encoding"], "chunked") == 0) { http_request_->has_content = true; chunked_decoder_ = std::make_unique<HttpChunkedDecoder>(); state_ = STATE_CONTENT; diff --git a/net/third_party/quiche/src/WORKSPACE.bazel b/net/third_party/quiche/src/WORKSPACE.bazel index 8b8cc6ea9..62e2c6f75 100644 --- a/net/third_party/quiche/src/WORKSPACE.bazel +++ b/net/third_party/quiche/src/WORKSPACE.bazel @@ -18,9 +18,9 @@ http_archive( http_archive( name = "com_google_absl", - sha256 = "44634eae586a7158dceedda7d8fd5cec6d1ebae08c83399f75dd9ce76324de40", # Last updated 2022-05-18 - strip_prefix = "abseil-cpp-3e04aade4e7a53aebbbed1a1268117f1f522bfb0", - urls = ["https://github.com/abseil/abseil-cpp/archive/3e04aade4e7a53aebbbed1a1268117f1f522bfb0.zip"], + sha256 = "d33809a982df8705f5220d1acb7cc63650e692a12dc2a8ef3e68b8959a1cee02", # Last updated 2023-04-12 + strip_prefix = "abseil-cpp-32d314d0f5bb0ca3ff71ece49c71a728c128d43e", + urls = ["https://github.com/abseil/abseil-cpp/archive/32d314d0f5bb0ca3ff71ece49c71a728c128d43e.zip"], ) http_archive( @@ -62,9 +62,9 @@ http_archive( http_archive( name = "com_google_googletest", - sha256 = "7ee83802222f9392452c57b4757185697a51639b69b64590f2c2188f58618581", # Last updated 2022-05-18 - strip_prefix = "googletest-8d51dc50eb7e7698427fed81b85edad0e032112e", - urls = ["https://github.com/google/googletest/archive/8d51dc50eb7e7698427fed81b85edad0e032112e.zip"], + sha256 = "82808543c49488e712d9bd84c50edf40d692ffdaca552b4b019b8b533d3cf8ef", # Last updated 2023-04-12 + strip_prefix = "googletest-12a5852e451baabc79c63a86c634912c563d57bc", + urls = ["https://github.com/google/googletest/archive/12a5852e451baabc79c63a86c634912c563d57bc.zip"], ) # Note this must use a commit from the `abseil` branch of the RE2 project. diff --git a/net/third_party/quiche/src/build/source_list.bzl b/net/third_party/quiche/src/build/source_list.bzl index 3ef9b8b6b..6501eb8e2 100644 --- a/net/third_party/quiche/src/build/source_list.bzl +++ b/net/third_party/quiche/src/build/source_list.bzl @@ -20,6 +20,7 @@ quiche_core_hdrs = [ "balsa/noop_balsa_visitor.h", "balsa/simple_buffer.h", "balsa/standard_header_map.h", + "common/btree_scheduler.h", "common/capsule.h", "common/masque/connect_udp_datagram_payload.h", "common/platform/api/quiche_bug_tracker.h", @@ -1024,6 +1025,7 @@ quiche_tests_srcs = [ "balsa/header_properties_test.cc", "balsa/simple_buffer_test.cc", "binary_http/binary_http_message_test.cc", + "common/btree_scheduler_test.cc", "common/capsule_test.cc", "common/masque/connect_udp_datagram_payload_test.cc", "common/platform/api/quiche_file_utils_test.cc", @@ -1306,6 +1308,7 @@ fuzzers_hdrs = [ ] fuzzers_srcs = [ + "common/btree_scheduler_fuzzer.cc", "common/structured_headers_fuzzer.cc", "quic/core/crypto/certificate_view_der_fuzzer.cc", "quic/core/crypto/certificate_view_pem_fuzzer.cc", diff --git a/net/third_party/quiche/src/build/source_list.gni b/net/third_party/quiche/src/build/source_list.gni index cd0d0c19f..7d493d23c 100644 --- a/net/third_party/quiche/src/build/source_list.gni +++ b/net/third_party/quiche/src/build/source_list.gni @@ -20,6 +20,7 @@ quiche_core_hdrs = [ "src/quiche/balsa/noop_balsa_visitor.h", "src/quiche/balsa/simple_buffer.h", "src/quiche/balsa/standard_header_map.h", + "src/quiche/common/btree_scheduler.h", "src/quiche/common/capsule.h", "src/quiche/common/masque/connect_udp_datagram_payload.h", "src/quiche/common/platform/api/quiche_bug_tracker.h", @@ -1024,6 +1025,7 @@ quiche_tests_srcs = [ "src/quiche/balsa/header_properties_test.cc", "src/quiche/balsa/simple_buffer_test.cc", "src/quiche/binary_http/binary_http_message_test.cc", + "src/quiche/common/btree_scheduler_test.cc", "src/quiche/common/capsule_test.cc", "src/quiche/common/masque/connect_udp_datagram_payload_test.cc", "src/quiche/common/platform/api/quiche_file_utils_test.cc", @@ -1306,6 +1308,7 @@ fuzzers_hdrs = [ ] fuzzers_srcs = [ + "src/quiche/common/btree_scheduler_fuzzer.cc", "src/quiche/common/structured_headers_fuzzer.cc", "src/quiche/quic/core/crypto/certificate_view_der_fuzzer.cc", "src/quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc", diff --git a/net/third_party/quiche/src/build/source_list.json b/net/third_party/quiche/src/build/source_list.json index 561a1d455..1bd873646 100644 --- a/net/third_party/quiche/src/build/source_list.json +++ b/net/third_party/quiche/src/build/source_list.json @@ -19,6 +19,7 @@ "quiche/balsa/noop_balsa_visitor.h", "quiche/balsa/simple_buffer.h", "quiche/balsa/standard_header_map.h", + "quiche/common/btree_scheduler.h", "quiche/common/capsule.h", "quiche/common/masque/connect_udp_datagram_payload.h", "quiche/common/platform/api/quiche_bug_tracker.h", @@ -1023,6 +1024,7 @@ "quiche/balsa/header_properties_test.cc", "quiche/balsa/simple_buffer_test.cc", "quiche/binary_http/binary_http_message_test.cc", + "quiche/common/btree_scheduler_test.cc", "quiche/common/capsule_test.cc", "quiche/common/masque/connect_udp_datagram_payload_test.cc", "quiche/common/platform/api/quiche_file_utils_test.cc", @@ -1305,6 +1307,7 @@ ], "fuzzers_srcs": [ + "quiche/common/btree_scheduler_fuzzer.cc", "quiche/common/structured_headers_fuzzer.cc", "quiche/quic/core/crypto/certificate_view_der_fuzzer.cc", "quiche/quic/core/crypto/certificate_view_pem_fuzzer.cc", diff --git a/net/third_party/quiche/src/quiche/BUILD.bazel b/net/third_party/quiche/src/quiche/BUILD.bazel index 99e8fc5d7..f63fb060e 100644 --- a/net/third_party/quiche/src/quiche/BUILD.bazel +++ b/net/third_party/quiche/src/quiche/BUILD.bazel @@ -345,6 +345,7 @@ test_suite_from_source_list( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", "@com_google_googleurl//url", ], diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc index ebe07225f..16dad4f9c 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc @@ -40,31 +40,21 @@ namespace anonymous_tokens { namespace internal { -BN_ULONG TOBN(BN_ULONG hi, BN_ULONG lo) { - return ((BN_ULONG)(hi) << 32 | (lo)); -} - // Approximation of sqrt(2) taken from // //depot/google3/third_party/openssl/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c;l=997 -const BN_ULONG kBoringSSLRSASqrtTwo[] = { - TOBN(0x4d7c60a5, 0xe633e3e1), TOBN(0x5fcf8f7b, 0xca3ea33b), - TOBN(0xc246785e, 0x92957023), TOBN(0xf9acce41, 0x797f2805), - TOBN(0xfdfe170f, 0xd3b1f780), TOBN(0xd24f4a76, 0x3facb882), - TOBN(0x18838a2e, 0xaff5f3b2), TOBN(0xc1fcbdde, 0xa2f7dc33), - TOBN(0xdea06241, 0xf7aa81c2), TOBN(0xf6a1be3f, 0xca221307), - TOBN(0x332a5e9f, 0x7bda1ebf), TOBN(0x0104dc01, 0xfe32352f), - TOBN(0xb8cf341b, 0x6f8236c7), TOBN(0x4264dabc, 0xd528b651), - TOBN(0xf4d3a02c, 0xebc93e0c), TOBN(0x81394ab6, 0xd8fd0efd), - TOBN(0xeaa4a089, 0x9040ca4a), TOBN(0xf52f120f, 0x836e582e), - TOBN(0xcb2a6343, 0x31f3c84d), TOBN(0xc6d5a8a3, 0x8bb7e9dc), - TOBN(0x460abc72, 0x2f7c4e33), TOBN(0xcab1bc91, 0x1688458a), - TOBN(0x53059c60, 0x11bc337b), TOBN(0xd2202e87, 0x42af1f4e), - TOBN(0x78048736, 0x3dfa2768), TOBN(0x0f74a85e, 0x439c7b4a), - TOBN(0xa8b1fe6f, 0xdc83db39), TOBN(0x4afc8304, 0x3ab8a2c3), - TOBN(0xed17ac85, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c), - TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484), +const std::vector<uint32_t> kBoringSSLRSASqrtTwo = { + 0x4d7c60a5, 0xe633e3e1, 0x5fcf8f7b, 0xca3ea33b, 0xc246785e, 0x92957023, + 0xf9acce41, 0x797f2805, 0xfdfe170f, 0xd3b1f780, 0xd24f4a76, 0x3facb882, + 0x18838a2e, 0xaff5f3b2, 0xc1fcbdde, 0xa2f7dc33, 0xdea06241, 0xf7aa81c2, + 0xf6a1be3f, 0xca221307, 0x332a5e9f, 0x7bda1ebf, 0x0104dc01, 0xfe32352f, + 0xb8cf341b, 0x6f8236c7, 0x4264dabc, 0xd528b651, 0xf4d3a02c, 0xebc93e0c, + 0x81394ab6, 0xd8fd0efd, 0xeaa4a089, 0x9040ca4a, 0xf52f120f, 0x836e582e, + 0xcb2a6343, 0x31f3c84d, 0xc6d5a8a3, 0x8bb7e9dc, 0x460abc72, 0x2f7c4e33, + 0xcab1bc91, 0x1688458a, 0x53059c60, 0x11bc337b, 0xd2202e87, 0x42af1f4e, + 0x78048736, 0x3dfa2768, 0x0f74a85e, 0x439c7b4a, 0xa8b1fe6f, 0xdc83db39, + 0x4afc8304, 0x3ab8a2c3, 0xed17ac85, 0x83339915, 0x1d6f60ba, 0x893ba84c, + 0x597d89b3, 0x754abe9f, 0xb504f333, 0xf9de6484, }; -const int kBoringSSLRSASqrtTwoLen = 32; absl::StatusOr<bssl::UniquePtr<BIGNUM>> PublicMetadataHashWithHKDF( absl::string_view public_metadata, absl::string_view rsa_modulus_str, @@ -224,13 +214,24 @@ absl::StatusOr<const EVP_MD*> ProtoMaskGenFunctionToEVPDigest( absl::StatusOr<bssl::UniquePtr<BIGNUM>> GetRsaSqrtTwo(int x) { // Compute hard-coded sqrt(2). ANON_TOKENS_ASSIGN_OR_RETURN(bssl::UniquePtr<BIGNUM> sqrt2, NewBigNum()); - for (int i = internal::kBoringSSLRSASqrtTwoLen - 1; i >= 0; --i) { + // TODO(b/277606961): simplify RsaSqrtTwo initialization logic + for (int i = internal::kBoringSSLRSASqrtTwo.size() - 2; i >= 0; i = i - 2) { + // Add the uint32_t values as words directly and shift. + // 'i' is the "hi" value of a uint64_t, and 'i+1' is the "lo" value. if (BN_add_word(sqrt2.get(), internal::kBoringSSLRSASqrtTwo[i]) != 1) { return absl::InternalError(absl::StrCat( "Cannot add word to compute RSA sqrt(2): ", GetSslErrors())); } + if (BN_lshift(sqrt2.get(), sqrt2.get(), 32) != 1) { + return absl::InternalError(absl::StrCat( + "Cannot shift to compute RSA sqrt(2): ", GetSslErrors())); + } + if (BN_add_word(sqrt2.get(), internal::kBoringSSLRSASqrtTwo[i+1]) != 1) { + return absl::InternalError(absl::StrCat( + "Cannot add word to compute RSA sqrt(2): ", GetSslErrors())); + } if (i > 0) { - if (BN_lshift(sqrt2.get(), sqrt2.get(), 64) != 1) { + if (BN_lshift(sqrt2.get(), sqrt2.get(), 32) != 1) { return absl::InternalError(absl::StrCat( "Cannot shift to compute RSA sqrt(2): ", GetSslErrors())); } @@ -238,7 +239,7 @@ absl::StatusOr<bssl::UniquePtr<BIGNUM>> GetRsaSqrtTwo(int x) { } // Check that hard-coded result is correct length. - int sqrt2_bits = 64 * internal::kBoringSSLRSASqrtTwoLen; + int sqrt2_bits = 32 * internal::kBoringSSLRSASqrtTwo.size(); if (BN_num_bits(sqrt2.get()) != sqrt2_bits) { return absl::InternalError("RSA sqrt(2) is not correct length."); } diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blind_signer.h b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blind_signer.h index 60fa56575..c1abec14a 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blind_signer.h +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blind_signer.h @@ -60,7 +60,7 @@ class QUICHE_EXPORT RsaBlindSigner : public BlindSigner { absl::StatusOr<std::string> SignInternal(absl::string_view input) const; - const std::optional<absl::string_view> public_metadata_; + const std::optional<std::string> public_metadata_; // We only keep these for the case when we use RSA blind signatures with // public metadata. Specifically augmented_rsa_e_ and augmented_rsa_d_ is diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blinder.h b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blinder.h index 5663c7937..519fbc2eb 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blinder.h +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_blinder.h @@ -68,7 +68,7 @@ class QUICHE_EXPORT RsaBlinder : public Blinder { bssl::UniquePtr<BN_MONT_CTX> mont_n); const int salt_length_; - std::optional<absl::string_view> public_metadata_; + std::optional<std::string> public_metadata_; const EVP_MD* sig_hash_; // Owned by BoringSSL. const EVP_MD* mgf1_hash_; // Owned by BoringSSL. diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_ssa_pss_verifier.h b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_ssa_pss_verifier.h index e97fed859..569abe895 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_ssa_pss_verifier.h +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/rsa_ssa_pss_verifier.h @@ -59,7 +59,7 @@ class QUICHE_EXPORT RsaSsaPssVerifier : public Verifier { bssl::UniquePtr<BIGNUM> augmented_rsa_e); const int salt_length_; - std::optional<absl::string_view> public_metadata_; + std::optional<std::string> public_metadata_; const EVP_MD* sig_hash_; // Owned by BoringSSL. const EVP_MD* mgf1_hash_; // Owned by BoringSSL. diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/testing/utils.cc b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/testing/utils.cc index 8f8afaf2a..75a12eda6 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/testing/utils.cc +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/anonymous_tokens/cpp/testing/utils.cc @@ -17,6 +17,7 @@ #include <stdint.h> #include <fstream> +#include <ios> #include <memory> #include <random> #include <sstream> @@ -43,21 +44,22 @@ namespace anonymous_tokens { namespace { absl::StatusOr<std::string> ReadFileToString(absl::string_view path) { - std::ifstream file((std::string(path))); + std::ifstream file(std::string(path), std::ios::binary); if (!file.is_open()) { return absl::InternalError("Reading file failed."); } - std::ostringstream ss; + std::ostringstream ss(std::ios::binary); ss << file.rdbuf(); return ss.str(); } absl::StatusOr<std::pair<RSAPublicKey, RSAPrivateKey>> ParseRsaKeysFromFile( absl::string_view path) { - ANON_TOKENS_ASSIGN_OR_RETURN(std::string text_proto, ReadFileToString(path)); + ANON_TOKENS_ASSIGN_OR_RETURN(std::string binary_proto, + ReadFileToString(path)); RSAPrivateKey private_key; - if (!private_key.ParseFromString(text_proto)) { - return absl::InternalError("Parsing text proto failed."); + if (!private_key.ParseFromString(binary_proto)) { + return absl::InternalError("Parsing binary proto failed."); } RSAPublicKey public_key; public_key.set_n(private_key.n()); diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth.cc b/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth.cc index aa6e5b259..ed7cbd099 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth.cc +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth.cc @@ -45,7 +45,7 @@ void BlindSignAuth::GetTokens( request.set_use_attestation(false); request.set_service_type("chromeipblinding"); request.set_location_granularity( - privacy::ppn::GetInitialDataRequest_LocationGranularity_UNKNOWN); + privacy::ppn::GetInitialDataRequest_LocationGranularity_CITY_GEOS); // Call GetInitialData on the HttpFetcher. std::string path_and_query = "/v1/getInitialData"; @@ -276,13 +276,16 @@ absl::Status BlindSignAuth::FingerprintPublicMetadata( uint32_t digest_length = 0; // Concatenate fields in tag number order, omitting fields whose values match // the default. This enables new fields to be added without changing the - // resulting encoding. - const std::string input = absl::StrCat( // - metadata.exit_location().country(), // - metadata.exit_location().city_geo_id(), // - metadata.service_type(), // - OmitDefault(metadata.expiration().seconds()), // - OmitDefault(metadata.expiration().nanos())); + // resulting encoding. The signer needs to ensure that | is not allowed in any + // metadata value so intentional collisions cannot be created. + const std::vector<std::string> parts = { + metadata.exit_location().country(), + metadata.exit_location().city_geo_id(), + metadata.service_type(), + OmitDefault(metadata.expiration().seconds()), + OmitDefault(metadata.expiration().nanos()), + }; + const std::string input = absl::StrJoin(parts, "|"); if (EVP_Digest(input.data(), input.length(), reinterpret_cast<uint8_t*>(&digest[0]), &digest_length, hasher, nullptr) != 1) { diff --git a/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth_test.cc b/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth_test.cc index 1e2270eb4..5f0dc1ec7 100644 --- a/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth_test.cc +++ b/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth_test.cc @@ -56,7 +56,7 @@ class BlindSignAuthTest : public QuicheTest { expected_get_initial_data_request_.set_use_attestation(false); expected_get_initial_data_request_.set_service_type("chromeipblinding"); expected_get_initial_data_request_.set_location_granularity( - privacy::ppn::GetInitialDataRequest_LocationGranularity_UNKNOWN); + privacy::ppn::GetInitialDataRequest_LocationGranularity_CITY_GEOS); // Create fake public key response. privacy::ppn::GetInitialDataResponse fake_get_initial_data_response; diff --git a/net/third_party/quiche/src/quiche/common/btree_scheduler.h b/net/third_party/quiche/src/quiche/common/btree_scheduler.h new file mode 100644 index 000000000..fd07d36a3 --- /dev/null +++ b/net/third_party/quiche/src/quiche/common/btree_scheduler.h @@ -0,0 +1,297 @@ +// Copyright 2023 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. + +#ifndef QUICHE_COMMON_BTREE_SCHEDULER_H_ +#define QUICHE_COMMON_BTREE_SCHEDULER_H_ + +#include <limits> + +#include "absl/container/btree_map.h" +#include "absl/container/node_hash_map.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/types/optional.h" +#include "quiche/common/platform/api/quiche_bug_tracker.h" +#include "quiche/common/platform/api/quiche_export.h" + +namespace quiche { + +// BTreeScheduler is a data structure that allows streams (and potentially other +// entities) to be scheduled according to the arbitrary priorities. The API for +// using the scheduler can be used as follows: +// - A stream has to be registered with a priority before being scheduled. +// - A stream can be unregistered, or can be re-prioritized. +// - A stream can be scheduled; that adds it into the queue. +// - PopFront() will return the stream with highest priority. +// - ShouldYield() will return if there is a stream with higher priority than +// the specified one. +// +// The prioritization works as following: +// - If two streams have different priorities, the higher priority stream goes +// first. +// - If two streams have the same priority, the one that got scheduled earlier +// goes first. Internally, this is implemented by assigning a monotonically +// decreasing sequence number to every newly scheduled stream. +// +// The Id type has to define operator==, be hashable via absl::Hash, and +// printable via operator<<; the Priority type has to define operator<. +template <typename Id, typename Priority> +class QUICHE_EXPORT BTreeScheduler { + public: + // Returns true if there are any streams scheduled. + bool HasScheduled() const { return !schedule_.empty(); } + // Returns the number of currently scheduled streams. + size_t NumScheduled() const { return schedule_.size(); } + + // Counts the number of scheduled entries in the range [min, max]. If either + // min or max is omitted, negative or positive infinity is assumed. + size_t NumScheduledInPriorityRange(absl::optional<Priority> min, + absl::optional<Priority> max) const; + + // Returns true if there is a stream that would go before `id` in the + // schedule. + absl::StatusOr<bool> ShouldYield(Id id) const; + + // Returns the priority for `id`, or nullopt if stream is not registered. + absl::optional<Priority> GetPriorityFor(Id id) const { + auto it = streams_.find(id); + if (it == streams_.end()) { + return absl::nullopt; + } + return it->second.priority; + } + + // Pops the highest priority stream. Will fail if the schedule is empty. + absl::StatusOr<Id> PopFront(); + + // Registers the specified stream with the supplied priority. The stream must + // not be already registered. + absl::Status Register(Id stream_id, const Priority& priority); + // Unregisters a previously registered stream. + absl::Status Unregister(Id stream_id); + // Alters the priority of an already registered stream. + absl::Status UpdatePriority(Id stream_id, const Priority& new_priority); + + // Adds the `stream` into the schedule if it's not already there. + absl::Status Schedule(Id stream_id); + // Returns true if `stream` is in the schedule. + bool IsScheduled(Id stream_id) const; + + private: + // A record for a registered stream. + struct StreamEntry { + // The current priority of the stream. + Priority priority; + // If present, the sequence number with which the stream is currently + // scheduled. If absent, indicates that the stream is not scheduled. + absl::optional<int> current_sequence_number; + + bool scheduled() const { return current_sequence_number.has_value(); } + }; + // The full entry for the stream (includes the ID that's used as a hashmap + // key). + using FullStreamEntry = std::pair<const Id, StreamEntry>; + + // A key that is used to order entities within the schedule. + struct ScheduleKey { + // The main order key: the priority of the stream. + Priority priority; + // The secondary order key: the sequence number. + int sequence_number; + + // Orders schedule keys in order of decreasing priority. + bool operator<(const ScheduleKey& other) const { + return std::make_tuple(priority, sequence_number) > + std::make_tuple(other.priority, other.sequence_number); + } + + // In order to find all entities with priority `p`, one can iterate between + // `lower_bound(MinForPriority(p))` and `upper_bound(MaxForPriority(p))`. + static ScheduleKey MinForPriority(Priority priority) { + return ScheduleKey{priority, std::numeric_limits<int>::max()}; + } + static ScheduleKey MaxForPriority(Priority priority) { + return ScheduleKey{priority, std::numeric_limits<int>::min()}; + } + }; + using FullScheduleEntry = std::pair<const ScheduleKey, FullStreamEntry*>; + using ScheduleIterator = + typename absl::btree_map<ScheduleKey, FullStreamEntry*>::const_iterator; + + // Convenience method to get the stream ID for a schedule entry. + static Id StreamId(const FullScheduleEntry& entry) { + return entry.second->first; + } + + // Removes a stream from the schedule, and returns the old entry if it were + // present. + absl::StatusOr<FullScheduleEntry> DescheduleStream(const StreamEntry& entry); + + // The map of currently registered streams. + absl::node_hash_map<Id, StreamEntry> streams_; + // The stream schedule, ordered starting from the highest priority stream. + absl::btree_map<ScheduleKey, FullStreamEntry*> schedule_; + + // The counter that is used to ensure that streams with the same priority are + // handled in the FIFO order. Decreases with every write. + int current_write_sequence_number_ = 0; +}; + +template <typename Id, typename Priority> +size_t BTreeScheduler<Id, Priority>::NumScheduledInPriorityRange( + absl::optional<Priority> min, absl::optional<Priority> max) const { + if (min.has_value() && max.has_value()) { + QUICHE_DCHECK(*min <= *max); + } + // This is reversed, since the schedule is ordered in the descending priority + // order. + ScheduleIterator begin = + max.has_value() ? schedule_.lower_bound(ScheduleKey::MinForPriority(*max)) + : schedule_.begin(); + ScheduleIterator end = + min.has_value() ? schedule_.upper_bound(ScheduleKey::MaxForPriority(*min)) + : schedule_.end(); + return end - begin; +} + +template <typename Id, typename Priority> +absl::Status BTreeScheduler<Id, Priority>::Register(Id stream_id, + const Priority& priority) { + auto [it, success] = streams_.insert({stream_id, StreamEntry{priority}}); + if (!success) { + return absl::AlreadyExistsError("ID already registered"); + } + return absl::OkStatus(); +} + +template <typename Id, typename Priority> +auto BTreeScheduler<Id, Priority>::DescheduleStream(const StreamEntry& entry) + -> absl::StatusOr<FullScheduleEntry> { + QUICHE_DCHECK(entry.scheduled()); + auto it = schedule_.find( + ScheduleKey{entry.priority, *entry.current_sequence_number}); + if (it == schedule_.end()) { + return absl::InternalError( + "Calling DescheduleStream() on an entry that is not in the schedule at " + "the expected key."); + } + FullScheduleEntry result = *it; + schedule_.erase(it); + return result; +} + +template <typename Id, typename Priority> +absl::Status BTreeScheduler<Id, Priority>::Unregister(Id stream_id) { + auto it = streams_.find(stream_id); + if (it == streams_.end()) { + return absl::NotFoundError("Stream not registered"); + } + const StreamEntry& stream = it->second; + + if (stream.scheduled()) { + if (!DescheduleStream(stream).ok()) { + QUICHE_BUG(BTreeSchedule_Unregister_NotInSchedule) + << "UnregisterStream() called on a stream ID " << stream_id + << ", which is marked ready, but is not in the schedule"; + } + } + + streams_.erase(it); + return absl::OkStatus(); +} + +template <typename Id, typename Priority> +absl::Status BTreeScheduler<Id, Priority>::UpdatePriority( + Id stream_id, const Priority& new_priority) { + auto it = streams_.find(stream_id); + if (it == streams_.end()) { + return absl::NotFoundError("ID not registered"); + } + + StreamEntry& stream = it->second; + absl::optional<int> sequence_number; + if (stream.scheduled()) { + absl::StatusOr<FullScheduleEntry> old_entry = DescheduleStream(stream); + if (old_entry.ok()) { + sequence_number = old_entry->first.sequence_number; + QUICHE_DCHECK_EQ(old_entry->second, &*it); + } else { + QUICHE_BUG(BTreeScheduler_Update_Not_In_Schedule) + << "UpdatePriority() called on a stream ID " << stream_id + << ", which is marked ready, but is not in the schedule"; + } + } + + stream.priority = new_priority; + if (sequence_number.has_value()) { + schedule_.insert({ScheduleKey{stream.priority, *sequence_number}, &*it}); + } + return absl::OkStatus(); +} + +template <typename Id, typename Priority> +absl::StatusOr<bool> BTreeScheduler<Id, Priority>::ShouldYield( + Id stream_id) const { + const auto stream_it = streams_.find(stream_id); + if (stream_it == streams_.end()) { + return absl::NotFoundError("ID not registered"); + } + const StreamEntry& stream = stream_it->second; + + if (schedule_.empty()) { + return false; + } + const FullScheduleEntry& next = *schedule_.begin(); + if (StreamId(next) == stream_id) { + return false; + } + return next.first.priority >= stream.priority; +} + +template <typename Id, typename Priority> +absl::StatusOr<Id> BTreeScheduler<Id, Priority>::PopFront() { + if (schedule_.empty()) { + return absl::NotFoundError("No streams scheduled"); + } + auto schedule_it = schedule_.begin(); + QUICHE_DCHECK(schedule_it->second->second.scheduled()); + schedule_it->second->second.current_sequence_number = absl::nullopt; + + Id result = StreamId(*schedule_it); + schedule_.erase(schedule_it); + return result; +} + +template <typename Id, typename Priority> +absl::Status BTreeScheduler<Id, Priority>::Schedule(Id stream_id) { + const auto stream_it = streams_.find(stream_id); + if (stream_it == streams_.end()) { + return absl::NotFoundError("ID not registered"); + } + if (stream_it->second.scheduled()) { + return absl::OkStatus(); + } + auto [schedule_it, success] = + schedule_.insert({ScheduleKey{stream_it->second.priority, + --current_write_sequence_number_}, + &*stream_it}); + QUICHE_BUG_IF(WebTransportWriteBlockedList_AddStream_conflict, !success) + << "Conflicting key in scheduler for stream " << stream_id; + stream_it->second.current_sequence_number = + schedule_it->first.sequence_number; + return absl::OkStatus(); +} + +template <typename Id, typename Priority> +bool BTreeScheduler<Id, Priority>::IsScheduled(Id stream_id) const { + const auto stream_it = streams_.find(stream_id); + if (stream_it == streams_.end()) { + return false; + } + return stream_it->second.scheduled(); +} + +} // namespace quiche + +#endif // QUICHE_COMMON_BTREE_SCHEDULER_H_ diff --git a/net/third_party/quiche/src/quiche/common/btree_scheduler_fuzzer.cc b/net/third_party/quiche/src/quiche/common/btree_scheduler_fuzzer.cc new file mode 100644 index 000000000..eb7164554 --- /dev/null +++ b/net/third_party/quiche/src/quiche/common/btree_scheduler_fuzzer.cc @@ -0,0 +1,46 @@ +// Copyright 2023 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 "quiche/common/btree_scheduler.h" +#include "quiche/common/quiche_data_reader.h" + +namespace { +uint8_t ReadUint8(quiche::QuicheDataReader& reader) { + uint8_t result; + if (!reader.ReadUInt8(&result)) { + exit(0); + } + return result; +} +} // namespace + +// Simple fuzzer that attempts to drive the scheduler into an invalid state that +// would cause a QUICHE_BUG or a crash. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data_ptr, size_t size) { + quiche::BTreeScheduler<uint8_t, uint8_t> scheduler; + quiche::QuicheDataReader reader(reinterpret_cast<const char*>(data_ptr), + size); + while (!reader.IsDoneReading()) { + switch (ReadUint8(reader)) { + case 0: + (void)scheduler.Register(ReadUint8(reader), ReadUint8(reader)); + break; + case 1: + (void)scheduler.Unregister(ReadUint8(reader)); + break; + case 2: + (void)scheduler.UpdatePriority(ReadUint8(reader), ReadUint8(reader)); + break; + case 3: + (void)scheduler.Schedule(ReadUint8(reader)); + break; + case 4: + (void)scheduler.PopFront(); + break; + default: + return 0; + } + } + return 0; +} diff --git a/net/third_party/quiche/src/quiche/common/btree_scheduler_test.cc b/net/third_party/quiche/src/quiche/common/btree_scheduler_test.cc new file mode 100644 index 000000000..d3a806c55 --- /dev/null +++ b/net/third_party/quiche/src/quiche/common/btree_scheduler_test.cc @@ -0,0 +1,281 @@ +// Copyright 2023 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 "quiche/common/btree_scheduler.h" + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" +#include "quiche/common/platform/api/quiche_test.h" +#include "quiche/common/test_tools/quiche_test_utils.h" + +namespace quiche::test { +namespace { + +using ::testing::ElementsAre; +using ::testing::Optional; + +template <typename Id, typename Priority> +void ScheduleIds(BTreeScheduler<Id, Priority>& scheduler, + absl::Span<const Id> ids) { + for (Id id : ids) { + QUICHE_EXPECT_OK(scheduler.Schedule(id)); + } +} + +template <typename Id, typename Priority> +std::vector<Id> PopAll(BTreeScheduler<Id, Priority>& scheduler) { + std::vector<Id> result; + result.reserve(scheduler.NumScheduled()); + for (;;) { + absl::StatusOr<Id> id = scheduler.PopFront(); + if (id.ok()) { + result.push_back(*id); + } else { + EXPECT_THAT(id, StatusIs(absl::StatusCode::kNotFound)); + break; + } + } + return result; +} + +TEST(BTreeSchedulerTest, SimplePop) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 100)); + QUICHE_EXPECT_OK(scheduler.Register(2, 101)); + QUICHE_EXPECT_OK(scheduler.Register(3, 102)); + + EXPECT_THAT(scheduler.GetPriorityFor(1), Optional(100)); + EXPECT_THAT(scheduler.GetPriorityFor(3), Optional(102)); + EXPECT_EQ(scheduler.GetPriorityFor(5), absl::nullopt); + + EXPECT_EQ(scheduler.NumScheduled(), 0u); + EXPECT_FALSE(scheduler.HasScheduled()); + QUICHE_EXPECT_OK(scheduler.Schedule(1)); + QUICHE_EXPECT_OK(scheduler.Schedule(2)); + QUICHE_EXPECT_OK(scheduler.Schedule(3)); + EXPECT_EQ(scheduler.NumScheduled(), 3u); + EXPECT_TRUE(scheduler.HasScheduled()); + + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(3)); + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(2)); + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(1)); + + QUICHE_EXPECT_OK(scheduler.Schedule(2)); + QUICHE_EXPECT_OK(scheduler.Schedule(1)); + QUICHE_EXPECT_OK(scheduler.Schedule(3)); + + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(3)); + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(2)); + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(1)); + + QUICHE_EXPECT_OK(scheduler.Schedule(3)); + QUICHE_EXPECT_OK(scheduler.Schedule(1)); + + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(3)); + EXPECT_THAT(scheduler.PopFront(), IsOkAndHolds(1)); +} + +TEST(BTreeSchedulerTest, FIFO) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 100)); + QUICHE_EXPECT_OK(scheduler.Register(2, 100)); + QUICHE_EXPECT_OK(scheduler.Register(3, 100)); + + ScheduleIds(scheduler, {2, 1, 3}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(2, 1, 3)); + + QUICHE_EXPECT_OK(scheduler.Register(4, 101)); + QUICHE_EXPECT_OK(scheduler.Register(5, 99)); + + ScheduleIds(scheduler, {5, 1, 2, 3, 4}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(4, 1, 2, 3, 5)); + ScheduleIds(scheduler, {1, 5, 2, 4, 3}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(4, 1, 2, 3, 5)); + ScheduleIds(scheduler, {3, 5, 2, 4, 1}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(4, 3, 2, 1, 5)); + ScheduleIds(scheduler, {3, 2, 1, 2, 3}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(3, 2, 1)); +} + +TEST(BTreeSchedulerTest, NumEntriesInRange) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + QUICHE_EXPECT_OK(scheduler.Register(3, 0)); + QUICHE_EXPECT_OK(scheduler.Register(4, -2)); + QUICHE_EXPECT_OK(scheduler.Register(5, -5)); + QUICHE_EXPECT_OK(scheduler.Register(6, 10)); + QUICHE_EXPECT_OK(scheduler.Register(7, 16)); + QUICHE_EXPECT_OK(scheduler.Register(8, 32)); + QUICHE_EXPECT_OK(scheduler.Register(9, 64)); + + EXPECT_EQ(scheduler.NumScheduled(), 0u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(absl::nullopt, absl::nullopt), + 0u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(-1, 1), 0u); + + for (int stream = 1; stream <= 9; ++stream) { + QUICHE_ASSERT_OK(scheduler.Schedule(stream)); + } + + EXPECT_EQ(scheduler.NumScheduled(), 9u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(absl::nullopt, absl::nullopt), + 9u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(0, 0), 3u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(absl::nullopt, -1), 2u); + EXPECT_EQ(scheduler.NumScheduledInPriorityRange(1, absl::nullopt), 4u); +} + +TEST(BTreeSchedulerTest, Registration) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + + QUICHE_EXPECT_OK(scheduler.Schedule(1)); + QUICHE_EXPECT_OK(scheduler.Schedule(2)); + EXPECT_EQ(scheduler.NumScheduled(), 2u); + EXPECT_TRUE(scheduler.IsScheduled(2)); + + EXPECT_THAT(scheduler.Register(2, 0), + StatusIs(absl::StatusCode::kAlreadyExists)); + QUICHE_EXPECT_OK(scheduler.Unregister(2)); + EXPECT_EQ(scheduler.NumScheduled(), 1u); + EXPECT_FALSE(scheduler.IsScheduled(2)); + + EXPECT_THAT(scheduler.UpdatePriority(2, 1234), + StatusIs(absl::StatusCode::kNotFound)); + EXPECT_THAT(scheduler.Unregister(2), StatusIs(absl::StatusCode::kNotFound)); + EXPECT_THAT(scheduler.Schedule(2), StatusIs(absl::StatusCode::kNotFound)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + EXPECT_EQ(scheduler.NumScheduled(), 1u); + EXPECT_TRUE(scheduler.IsScheduled(1)); + EXPECT_FALSE(scheduler.IsScheduled(2)); +} + +TEST(BTreeSchedulerTest, UpdatePriorityUp) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + QUICHE_EXPECT_OK(scheduler.Register(3, 0)); + + ScheduleIds(scheduler, {1, 2, 3}); + QUICHE_EXPECT_OK(scheduler.UpdatePriority(2, 1000)); + EXPECT_THAT(PopAll(scheduler), ElementsAre(2, 1, 3)); +} + +TEST(BTreeSchedulerTest, UpdatePriorityDown) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + QUICHE_EXPECT_OK(scheduler.Register(3, 0)); + + ScheduleIds(scheduler, {1, 2, 3}); + QUICHE_EXPECT_OK(scheduler.UpdatePriority(2, -1000)); + EXPECT_THAT(PopAll(scheduler), ElementsAre(1, 3, 2)); +} + +TEST(BTreeSchedulerTest, UpdatePriorityEqual) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, 0)); + QUICHE_EXPECT_OK(scheduler.Register(3, 0)); + + ScheduleIds(scheduler, {1, 2, 3}); + QUICHE_EXPECT_OK(scheduler.UpdatePriority(2, 0)); + EXPECT_THAT(PopAll(scheduler), ElementsAre(1, 2, 3)); +} + +TEST(BTreeSchedulerTest, UpdatePriorityIntoSameBucket) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(1, 0)); + QUICHE_EXPECT_OK(scheduler.Register(2, -100)); + QUICHE_EXPECT_OK(scheduler.Register(3, 0)); + + ScheduleIds(scheduler, {1, 2, 3}); + QUICHE_EXPECT_OK(scheduler.UpdatePriority(2, 0)); + EXPECT_THAT(PopAll(scheduler), ElementsAre(1, 2, 3)); +} + +TEST(BTreeSchedulerTest, ShouldYield) { + BTreeScheduler<int, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(10, 100)); + QUICHE_EXPECT_OK(scheduler.Register(20, 101)); + QUICHE_EXPECT_OK(scheduler.Register(21, 101)); + QUICHE_EXPECT_OK(scheduler.Register(30, 102)); + + EXPECT_THAT(scheduler.ShouldYield(10), IsOkAndHolds(false)); + EXPECT_THAT(scheduler.ShouldYield(20), IsOkAndHolds(false)); + EXPECT_THAT(scheduler.ShouldYield(21), IsOkAndHolds(false)); + EXPECT_THAT(scheduler.ShouldYield(30), IsOkAndHolds(false)); + EXPECT_THAT(scheduler.ShouldYield(40), StatusIs(absl::StatusCode::kNotFound)); + + QUICHE_EXPECT_OK(scheduler.Schedule(20)); + + EXPECT_THAT(scheduler.ShouldYield(10), IsOkAndHolds(true)); + EXPECT_THAT(scheduler.ShouldYield(20), IsOkAndHolds(false)); + EXPECT_THAT(scheduler.ShouldYield(21), IsOkAndHolds(true)); + EXPECT_THAT(scheduler.ShouldYield(30), IsOkAndHolds(false)); +} + +struct CustomPriority { + int a; + int b; + + bool operator<(const CustomPriority& other) const { + return std::make_tuple(a, b) < std::make_tuple(other.a, other.b); + } +}; + +TEST(BTreeSchedulerTest, CustomPriority) { + BTreeScheduler<int, CustomPriority> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(10, CustomPriority{0, 1})); + QUICHE_EXPECT_OK(scheduler.Register(11, CustomPriority{0, 0})); + QUICHE_EXPECT_OK(scheduler.Register(12, CustomPriority{0, 0})); + QUICHE_EXPECT_OK(scheduler.Register(13, CustomPriority{10, 0})); + QUICHE_EXPECT_OK(scheduler.Register(14, CustomPriority{-10, 0})); + + ScheduleIds(scheduler, {10, 11, 12, 13, 14}); + EXPECT_THAT(PopAll(scheduler), ElementsAre(13, 10, 11, 12, 14)); +} + +struct CustomId { + int a; + std::string b; + + bool operator==(const CustomId& other) const { + return a == other.a && b == other.b; + } + + template <typename H> + friend H AbslHashValue(H h, const CustomId& c) { + return H::combine(std::move(h), c.a, c.b); + } +}; + +std::ostream& operator<<(std::ostream& os, const CustomId& id) { + os << id.a << ":" << id.b; + return os; +} + +TEST(BTreeSchedulerTest, CustomIds) { + BTreeScheduler<CustomId, int> scheduler; + QUICHE_EXPECT_OK(scheduler.Register(CustomId{1, "foo"}, 10)); + QUICHE_EXPECT_OK(scheduler.Register(CustomId{1, "bar"}, 12)); + QUICHE_EXPECT_OK(scheduler.Register(CustomId{2, "foo"}, 11)); + EXPECT_THAT(scheduler.Register(CustomId{1, "foo"}, 10), + StatusIs(absl::StatusCode::kAlreadyExists)); + + ScheduleIds(scheduler, + {CustomId{1, "foo"}, CustomId{1, "bar"}, CustomId{2, "foo"}}); + EXPECT_THAT(scheduler.ShouldYield(CustomId{1, "foo"}), IsOkAndHolds(true)); + EXPECT_THAT(scheduler.ShouldYield(CustomId{1, "bar"}), IsOkAndHolds(false)); + EXPECT_THAT( + PopAll(scheduler), + ElementsAre(CustomId{1, "bar"}, CustomId{2, "foo"}, CustomId{1, "foo"})); +} + +} // namespace +} // namespace quiche::test diff --git a/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.cc b/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.cc index ecea02403..e55df8a97 100644 --- a/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.cc +++ b/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.cc @@ -9,8 +9,6 @@ #include "quiche/quic/core/crypto/crypto_framer.h" #include "quiche/quic/core/crypto/crypto_handshake.h" #include "quiche/quic/core/crypto/crypto_handshake_message.h" -#include "quiche/quic/core/crypto/crypto_protocol.h" -#include "quiche/quic/core/crypto/crypto_utils.h" #include "quiche/quic/core/crypto/quic_decrypter.h" #include "quiche/quic/core/crypto/quic_encrypter.h" #include "quiche/quic/core/frames/quic_ack_frequency_frame.h" diff --git a/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.h b/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.h index 48a45f07c..fd83233c9 100644 --- a/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.h +++ b/net/third_party/quiche/src/quiche/quic/core/chlo_extractor.h @@ -11,7 +11,7 @@ namespace quic { // A utility for extracting QUIC Client Hello messages from packets, -// without needs to spin up a full QuicSession. +// without needing to spin up a full QuicSession. class QUIC_NO_EXPORT ChloExtractor { public: class QUIC_NO_EXPORT Delegate { diff --git a/net/third_party/quiche/src/quiche/quic/core/http/end_to_end_test.cc b/net/third_party/quiche/src/quiche/quic/core/http/end_to_end_test.cc index 4e2e5cfbd..35fe13a12 100644 --- a/net/third_party/quiche/src/quiche/quic/core/http/end_to_end_test.cc +++ b/net/third_party/quiche/src/quiche/quic/core/http/end_to_end_test.cc @@ -7182,10 +7182,11 @@ TEST_P(EndToEndTest, OriginalConnectionIdClearedFromMap) { server_thread_->Resume(); } -TEST_P(EndToEndTest, EcnMarksReportedCorrectly) { +TEST_P(EndToEndTest, ServerReportsEcn) { // Client connects using not-ECT. ASSERT_TRUE(Initialize()); QuicConnection* client_connection = GetClientConnection(); + QuicConnectionPeer::DisableEcnCodepointValidation(client_connection); QuicEcnCounts* ecn = QuicSentPacketManagerPeer::GetPeerEcnCounts( QuicConnectionPeer::GetSentPacketManager(client_connection), APPLICATION_DATA); @@ -7229,6 +7230,37 @@ TEST_P(EndToEndTest, EcnMarksReportedCorrectly) { client_->Disconnect(); } +TEST_P(EndToEndTest, ClientReportsEcn) { + ASSERT_TRUE(Initialize()); + // Wait for handshake to complete, so that we can manipulate the server + // connection without race conditions. + server_thread_->WaitForCryptoHandshakeConfirmed(); + QuicConnection* server_connection = GetServerConnection(); + QuicConnectionPeer::DisableEcnCodepointValidation(server_connection); + QuicEcnCounts* ecn = QuicSentPacketManagerPeer::GetPeerEcnCounts( + QuicConnectionPeer::GetSentPacketManager(server_connection), + APPLICATION_DATA); + TestPerPacketOptions options; + options.ecn_codepoint = ECN_ECT1; + server_connection->set_per_packet_options(&options); + client_->SendSynchronousRequest("/foo"); + // A second request provides a packet for the client ACKs to go with. + client_->SendSynchronousRequest("/foo"); + server_thread_->Pause(); + EXPECT_EQ(ecn->ect0, 0); + EXPECT_EQ(ecn->ce, 0); + if (!GetQuicRestartFlag(quic_receive_ecn) || + !GetQuicRestartFlag(quic_quiche_ecn_sockets) || + !VersionHasIetfQuicFrames(version_.transport_version)) { + EXPECT_EQ(ecn->ect1, 0); + } else { + EXPECT_GT(ecn->ect1, 0); + } + server_connection->set_per_packet_options(nullptr); + server_thread_->Resume(); + client_->Disconnect(); +} + TEST_P(EndToEndTest, ClientMigrationAfterHalfwayServerMigration) { use_preferred_address_ = true; ASSERT_TRUE(Initialize()); diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_connection.cc b/net/third_party/quiche/src/quiche/quic/core/quic_connection.cc index 271f17290..01e728aa7 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_connection.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_connection.cc @@ -26,7 +26,6 @@ #include "quiche/quic/core/crypto/crypto_utils.h" #include "quiche/quic/core/crypto/quic_decrypter.h" #include "quiche/quic/core/crypto/quic_encrypter.h" -#include "quiche/quic/core/proto/cached_network_parameters_proto.h" #include "quiche/quic/core/quic_bandwidth.h" #include "quiche/quic/core/quic_config.h" #include "quiche/quic/core/quic_connection_id.h" @@ -44,9 +43,7 @@ #include "quiche/quic/platform/api/quic_exported_stats.h" #include "quiche/quic/platform/api/quic_flag_utils.h" #include "quiche/quic/platform/api/quic_flags.h" -#include "quiche/quic/platform/api/quic_hostname_utils.h" #include "quiche/quic/platform/api/quic_logging.h" -#include "quiche/quic/platform/api/quic_server_stats.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/common/platform/api/quiche_flag_utils.h" #include "quiche/common/quiche_text_utils.h" @@ -3115,7 +3112,6 @@ bool QuicConnection::ValidateReceivedPacketNumber( void QuicConnection::WriteQueuedPackets() { QUICHE_DCHECK(!writer_->IsWriteBlocked()); - QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite", buffered_packets_.size(), 1, 1000, 50, ""); @@ -3124,7 +3120,7 @@ void QuicConnection::WriteQueuedPackets() { break; } const BufferedPacket& packet = buffered_packets_.front(); - WriteResult result = writer_->WritePacket( + WriteResult result = SendPacketToWriter( packet.data.get(), packet.length, packet.self_address.host(), packet.peer_address, per_packet_options_); QUIC_DVLOG(1) << ENDPOINT << "Sending buffered packet, result: " << result; @@ -3491,9 +3487,9 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { // // writer_->WritePacket transfers buffer ownership back to the writer. packet->release_encrypted_buffer = nullptr; - result = writer_->WritePacket(packet->encrypted_buffer, encrypted_length, - send_from_address.host(), send_to_address, - per_packet_options_); + result = SendPacketToWriter(packet->encrypted_buffer, encrypted_length, + send_from_address.host(), send_to_address, + per_packet_options_); // This is a work around for an issue with linux UDP GSO batch writers. // When sending a GSO packet with 2 segments, if the first segment is // larger than the path MTU, instead of EMSGSIZE, the linux kernel returns @@ -4148,6 +4144,38 @@ bool QuicConnection::IsKnownServerAddress( address) != known_server_addresses_.cend(); } +void QuicConnection::ClearEcnCodepoint() { + if (per_packet_options_ != nullptr) { + per_packet_options_->ecn_codepoint = ECN_NOT_ECT; + } +} + +WriteResult QuicConnection::SendPacketToWriter( + const char* buffer, size_t buf_len, const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, PerPacketOptions* options) { + if (!disable_ecn_codepoint_validation_) { + switch (GetNextEcnCodepoint()) { + case ECN_NOT_ECT: + break; + case ECN_ECT0: + if (!sent_packet_manager_.GetSendAlgorithm()->SupportsECT0()) { + ClearEcnCodepoint(); + } + break; + case ECN_ECT1: + if (!sent_packet_manager_.GetSendAlgorithm()->SupportsECT1()) { + ClearEcnCodepoint(); + } + break; + case ECN_CE: + ClearEcnCodepoint(); + break; + } + } + return writer_->WritePacket(buffer, buf_len, self_address, peer_address, + options); +} + void QuicConnection::OnRetransmissionTimeout() { ScopedRetransmissionTimeoutIndicator indicator(this); #ifndef NDEBUG @@ -5966,7 +5994,7 @@ bool QuicConnection::FlushCoalescedPacket() { buffer, static_cast<QuicPacketLength>(length), coalesced_packet_.self_address(), coalesced_packet_.peer_address()); } else { - WriteResult result = writer_->WritePacket( + WriteResult result = SendPacketToWriter( buffer, length, coalesced_packet_.self_address().host(), coalesced_packet_.peer_address(), per_packet_options_); if (IsWriteError(result.status)) { diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_connection.h b/net/third_party/quiche/src/quiche/quic/core/quic_connection.h index 79de6044e..55714ad50 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_connection.h +++ b/net/third_party/quiche/src/quiche/quic/core/quic_connection.h @@ -34,7 +34,6 @@ #include "quiche/quic/core/frames/quic_ack_frequency_frame.h" #include "quiche/quic/core/frames/quic_max_streams_frame.h" #include "quiche/quic/core/frames/quic_new_connection_id_frame.h" -#include "quiche/quic/core/proto/cached_network_parameters_proto.h" #include "quiche/quic/core/quic_alarm.h" #include "quiche/quic/core/quic_alarm_factory.h" #include "quiche/quic/core/quic_blocked_writer_interface.h" @@ -1961,6 +1960,22 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Returns true if |address| is known server address. bool IsKnownServerAddress(const QuicSocketAddress& address) const; + // Retrieves the ECN codepoint to be sent on the next packet. + QuicEcnCodepoint GetNextEcnCodepoint() const { + return (per_packet_options_ != nullptr) ? per_packet_options_->ecn_codepoint + : ECN_NOT_ECT; + } + + // Sets the ECN codepoint to Not-ECT. + void ClearEcnCodepoint(); + + // Writes the packet to the writer and clears the ECN codepoint in |options| + // if it is invalid. + WriteResult SendPacketToWriter(const char* buffer, size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options); + QuicConnectionContext context_; QuicFramer framer_; @@ -2357,6 +2372,14 @@ class QUIC_EXPORT_PRIVATE QuicConnection // original address. QuicLRUCache<QuicSocketAddress, bool, QuicSocketAddressHash> received_client_addresses_cache_; + + // Endpoints should never mark packets with Congestion Experienced (CE), as + // this is only done by routers. Endpoints cannot send ECT(0) or ECT(1) if + // their congestion control cannot respond to these signals in accordance with + // the spec, or if the QUIC implementation doesn't validate ECN feedback. When + // true, the connection will not verify that the requested codepoint adheres + // to these policies. This is only accessible through QuicConnectionPeer. + bool disable_ecn_codepoint_validation_ = false; }; } // namespace quic diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_connection_test.cc b/net/third_party/quiche/src/quiche/quic/core/quic_connection_test.cc index 27cf7066c..7853992a9 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_connection_test.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_connection_test.cc @@ -8,9 +8,7 @@ #include <cstdint> #include <memory> -#include <ostream> #include <string> -#include <type_traits> #include <utility> #include "absl/base/macros.h" @@ -22,7 +20,6 @@ #include "quiche/quic/core/crypto/null_decrypter.h" #include "quiche/quic/core/crypto/null_encrypter.h" #include "quiche/quic/core/crypto/quic_decrypter.h" -#include "quiche/quic/core/crypto/quic_encrypter.h" #include "quiche/quic/core/frames/quic_connection_close_frame.h" #include "quiche/quic/core/frames/quic_new_connection_id_frame.h" #include "quiche/quic/core/frames/quic_path_response_frame.h" @@ -56,7 +53,6 @@ #include "quiche/quic/test_tools/quic_test_utils.h" #include "quiche/quic/test_tools/simple_data_producer.h" #include "quiche/quic/test_tools/simple_session_notifier.h" -#include "quiche/common/platform/api/quiche_reference_counted.h" #include "quiche/common/simple_buffer_allocator.h" using testing::_; @@ -17465,6 +17461,57 @@ TEST_P(QuicConnectionTest, EXPECT_EQ(kSelfAddress, connection_.self_address()); } +TEST_P(QuicConnectionTest, EcnCodepointsRejected) { + TestPerPacketOptions per_packet_options; + connection_.set_per_packet_options(&per_packet_options); + for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) { + per_packet_options.ecn_codepoint = ecn; + if (ecn == ECN_ECT0) { + EXPECT_CALL(*send_algorithm_, SupportsECT0()).WillOnce(Return(false)); + } else if (ecn == ECN_ECT1) { + EXPECT_CALL(*send_algorithm_, SupportsECT1()).WillOnce(Return(false)); + } + EXPECT_CALL(connection_, OnSerializedPacket(_)); + SendPing(); + EXPECT_EQ(per_packet_options.ecn_codepoint, ECN_NOT_ECT); + EXPECT_EQ(writer_->last_ecn_sent(), ECN_NOT_ECT); + } +} + +TEST_P(QuicConnectionTest, EcnCodepointsAccepted) { + TestPerPacketOptions per_packet_options; + connection_.set_per_packet_options(&per_packet_options); + for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) { + per_packet_options.ecn_codepoint = ecn; + if (ecn == ECN_ECT0) { + EXPECT_CALL(*send_algorithm_, SupportsECT0()).WillOnce(Return(true)); + } else if (ecn == ECN_ECT1) { + EXPECT_CALL(*send_algorithm_, SupportsECT1()).WillOnce(Return(true)); + } + EXPECT_CALL(connection_, OnSerializedPacket(_)); + SendPing(); + QuicEcnCodepoint expected_codepoint = ecn; + if (ecn == ECN_CE) { + expected_codepoint = ECN_NOT_ECT; + } + EXPECT_EQ(per_packet_options.ecn_codepoint, expected_codepoint); + EXPECT_EQ(writer_->last_ecn_sent(), expected_codepoint); + } +} + +TEST_P(QuicConnectionTest, EcnValidationDisabled) { + TestPerPacketOptions per_packet_options; + connection_.set_per_packet_options(&per_packet_options); + QuicConnectionPeer::DisableEcnCodepointValidation(&connection_); + for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) { + per_packet_options.ecn_codepoint = ecn; + EXPECT_CALL(connection_, OnSerializedPacket(_)); + SendPing(); + EXPECT_EQ(per_packet_options.ecn_codepoint, ecn); + EXPECT_EQ(writer_->last_ecn_sent(), ecn); + } +} + } // namespace } // namespace test } // namespace quic diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher.cc b/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher.cc index 0f8c1abfe..9b1b470c3 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher.cc @@ -13,7 +13,6 @@ #include "absl/strings/string_view.h" #include "quiche/quic/core/chlo_extractor.h" #include "quiche/quic/core/crypto/crypto_protocol.h" -#include "quiche/quic/core/crypto/quic_random.h" #include "quiche/quic/core/quic_connection_id.h" #include "quiche/quic/core/quic_error_codes.h" #include "quiche/quic/core/quic_session.h" diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher_test.cc b/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher_test.cc index 3ed2330a9..12a62bb1d 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher_test.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_dispatcher_test.cc @@ -5,18 +5,15 @@ #include "quiche/quic/core/quic_dispatcher.h" #include <memory> -#include <ostream> #include <string> #include <utility> #include "absl/base/macros.h" #include "absl/strings/str_cat.h" #include "quiche/quic/core/chlo_extractor.h" -#include "quiche/quic/core/crypto/crypto_handshake.h" #include "quiche/quic/core/crypto/crypto_protocol.h" #include "quiche/quic/core/crypto/quic_crypto_server_config.h" #include "quiche/quic/core/crypto/quic_random.h" -#include "quiche/quic/core/frames/quic_new_connection_id_frame.h" #include "quiche/quic/core/quic_config.h" #include "quiche/quic/core/quic_connection.h" #include "quiche/quic/core/quic_connection_id.h" @@ -31,16 +28,13 @@ #include "quiche/quic/platform/api/quic_logging.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/crypto_test_utils.h" -#include "quiche/quic/test_tools/fake_proof_source.h" #include "quiche/quic/test_tools/first_flight.h" #include "quiche/quic/test_tools/mock_connection_id_generator.h" #include "quiche/quic/test_tools/mock_quic_time_wait_list_manager.h" #include "quiche/quic/test_tools/quic_buffered_packet_store_peer.h" #include "quiche/quic/test_tools/quic_connection_peer.h" -#include "quiche/quic/test_tools/quic_crypto_server_config_peer.h" #include "quiche/quic/test_tools/quic_dispatcher_peer.h" #include "quiche/quic/test_tools/quic_test_utils.h" -#include "quiche/quic/test_tools/quic_time_wait_list_manager_peer.h" #include "quiche/quic/tools/quic_simple_crypto_server_stream_helper.h" #include "quiche/common/test_tools/quiche_test_utils.h" diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_types.cc b/net/third_party/quiche/src/quiche/quic/core/quic_types.cc index e0460923d..3981256f6 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_types.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_types.cc @@ -446,6 +446,20 @@ QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, return os; } +std::string EcnCodepointToString(QuicEcnCodepoint ecn) { + switch (ecn) { + case ECN_NOT_ECT: + return "Not-ECT"; + case ECN_ECT0: + return "ECT(0)"; + case ECN_ECT1: + return "ECT(1)"; + case ECN_CE: + return "CE"; + } + return ""; // Handle compilation on windows for invalid enums +} + #undef RETURN_STRING_LITERAL // undef for jumbo builds } // namespace quic diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_types.h b/net/third_party/quiche/src/quiche/quic/core/quic_types.h index 6e2531b9b..b2a100b3b 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_types.h +++ b/net/third_party/quiche/src/quiche/quic/core/quic_types.h @@ -885,6 +885,8 @@ enum QuicEcnCodepoint { ECN_CE = 3, }; +QUICHE_EXPORT std::string EcnCodepointToString(QuicEcnCodepoint ecn); + // This struct reports the Explicit Congestion Notification (ECN) contents of // the ACK_ECN frame. They are the cumulative number of QUIC packets received // for that codepoint in a given Packet Number Space. diff --git a/net/third_party/quiche/src/quiche/quic/core/quic_utils_test.cc b/net/third_party/quiche/src/quiche/quic/core/quic_utils_test.cc index a4a194f5e..543c8ead0 100644 --- a/net/third_party/quiche/src/quiche/quic/core/quic_utils_test.cc +++ b/net/third_party/quiche/src/quiche/quic/core/quic_utils_test.cc @@ -233,6 +233,13 @@ TEST_F(QuicUtilsTest, StatelessResetToken) { EXPECT_FALSE(QuicUtils::AreStatelessResetTokensEqual(token1a, token2)); } +TEST_F(QuicUtilsTest, EcnCodepointToString) { + EXPECT_EQ(EcnCodepointToString(ECN_NOT_ECT), "Not-ECT"); + EXPECT_EQ(EcnCodepointToString(ECN_ECT0), "ECT(0)"); + EXPECT_EQ(EcnCodepointToString(ECN_ECT1), "ECT(1)"); + EXPECT_EQ(EcnCodepointToString(ECN_CE), "CE"); +} + enum class TestEnumClassBit : uint8_t { BIT_ZERO = 0, BIT_ONE, diff --git a/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.cc b/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.cc index 637f18f35..83f164843 100644 --- a/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.cc +++ b/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.cc @@ -72,7 +72,6 @@ MasqueClientSession::GetOrCreateConnectUdpClientState( auto it = fake_addresses_.find(target_server_address.host().ToPackedString()); if (it != fake_addresses_.end()) { target_host = it->second; - fake_addresses_.erase(it); } else { target_host = target_server_address.host().ToString(); } @@ -517,4 +516,9 @@ quiche::QuicheIpAddress MasqueClientSession::GetFakeAddress( return address; } +void MasqueClientSession::RemoveFakeAddress( + const quiche::QuicheIpAddress& fake_address) { + fake_addresses_.erase(fake_address.ToPackedString()); +} + } // namespace quic diff --git a/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.h b/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.h index 4b537b28d..4e317eba4 100644 --- a/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.h +++ b/net/third_party/quiche/src/quiche/quic/masque/masque_client_session.h @@ -117,9 +117,13 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { void CloseConnectIpStream(EncapsulatedIpSession* encapsulated_ip_session); // Generate a random Unique Local Address and register a mapping from - // that address to the corresponding hostname. + // that address to the corresponding hostname. The returned address should be + // removed by calling RemoveFakeAddress() once it is no longer needed. quiche::QuicheIpAddress GetFakeAddress(absl::string_view hostname); + // Removes a fake address that was previously created by GetFakeAddress(). + void RemoveFakeAddress(const quiche::QuicheIpAddress& fake_address); + private: // State that the MasqueClientSession keeps for each CONNECT-UDP request. class QUIC_NO_EXPORT ConnectUdpClientState diff --git a/net/third_party/quiche/src/quiche/quic/masque/masque_client_tools.cc b/net/third_party/quiche/src/quiche/quic/masque/masque_client_tools.cc index d77e94dc9..841052911 100644 --- a/net/third_party/quiche/src/quiche/quic/masque/masque_client_tools.cc +++ b/net/third_party/quiche/src/quiche/quic/masque/masque_client_tools.cc @@ -4,6 +4,7 @@ #include "quiche/quic/masque/masque_client_tools.h" +#include "absl/types/optional.h" #include "quiche/quic/masque/masque_encapsulated_client.h" #include "quiche/quic/masque/masque_utils.h" #include "quiche/quic/platform/api/quic_default_proof_providers.h" @@ -15,6 +16,33 @@ namespace quic { namespace tools { +namespace { + +// Helper class to ensure a fake address gets properly removed when this goes +// out of scope. +class FakeAddressRemover { + public: + FakeAddressRemover() = default; + void IngestFakeAddress(const quiche::QuicheIpAddress& fake_address, + MasqueClientSession* masque_client_session) { + QUICHE_CHECK(masque_client_session != nullptr); + QUICHE_CHECK(!fake_address_.has_value()); + fake_address_ = fake_address; + masque_client_session_ = masque_client_session; + } + ~FakeAddressRemover() { + if (fake_address_.has_value()) { + masque_client_session_->RemoveFakeAddress(*fake_address_); + } + } + + private: + absl::optional<quiche::QuicheIpAddress> fake_address_; + MasqueClientSession* masque_client_session_ = nullptr; +}; + +} // namespace + bool SendEncapsulatedMasqueRequest(MasqueClient* masque_client, QuicEventLoop* event_loop, std::string url_string, @@ -31,6 +59,7 @@ bool SendEncapsulatedMasqueRequest(MasqueClient* masque_client, // Build the client, and try to connect. QuicSocketAddress addr; + FakeAddressRemover fake_address_remover; if (dns_on_client) { addr = LookupAddress(address_family_for_lookup, url.host(), absl::StrCat(url.port())); @@ -39,9 +68,11 @@ bool SendEncapsulatedMasqueRequest(MasqueClient* masque_client, return false; } } else { - addr = QuicSocketAddress( - masque_client->masque_client_session()->GetFakeAddress(url.host()), - url.port()); + quiche::QuicheIpAddress fake_address = + masque_client->masque_client_session()->GetFakeAddress(url.host()); + fake_address_remover.IngestFakeAddress( + fake_address, masque_client->masque_client_session()); + addr = QuicSocketAddress(fake_address, url.port()); QUICHE_CHECK(addr.IsInitialized()); } const QuicServerId server_id(url.host(), url.port()); diff --git a/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.cc b/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.cc index 038f8d73b..8bddb19ab 100644 --- a/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.cc +++ b/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.cc @@ -618,5 +618,11 @@ bool QuicConnectionPeer::TestLastReceivedPacketInfoDefaults() { sizeof(QuicConnection::ReceivedPacketInfo) == 280); } +// static +void QuicConnectionPeer::DisableEcnCodepointValidation( + QuicConnection* connection) { + connection->disable_ecn_codepoint_validation_ = true; +} + } // namespace test } // namespace quic diff --git a/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h b/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h index 087fcc012..cf8a5a6cf 100644 --- a/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h +++ b/net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h @@ -241,6 +241,9 @@ class QuicConnectionPeer { QuicConnection* connection); static bool TestLastReceivedPacketInfoDefaults(); + + // Overrides restrictions on sending ECN for test purposes. + static void DisableEcnCodepointValidation(QuicConnection* connection); }; } // namespace test diff --git a/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.cc b/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.cc index 7eac0215c..e622ed816 100644 --- a/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.cc +++ b/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.cc @@ -1304,7 +1304,7 @@ TestPacketWriter::~TestPacketWriter() { WriteResult TestPacketWriter::WritePacket(const char* buffer, size_t buf_len, const QuicIpAddress& self_address, const QuicSocketAddress& peer_address, - PerPacketOptions* /*options*/) { + PerPacketOptions* options) { last_write_source_address_ = self_address; last_write_peer_address_ = peer_address; // If the buffer is allocated from the pool, return it back to the pool. @@ -1377,6 +1377,7 @@ WriteResult TestPacketWriter::WritePacket(const char* buffer, size_t buf_len, bytes_buffered_ += last_packet_size_; return WriteResult(WRITE_STATUS_OK, 0); } + last_ecn_sent_ = (options == nullptr) ? ECN_NOT_ECT : options->ecn_codepoint; return WriteResult(WRITE_STATUS_OK, last_packet_size_); } diff --git a/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h b/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h index 6d7e39544..2835ac167 100644 --- a/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h +++ b/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h @@ -2010,6 +2010,8 @@ class TestPacketWriter : public QuicPacketWriter { return last_write_peer_address_; } + QuicEcnCodepoint last_ecn_sent() const { return last_ecn_sent_; } + private: char* AllocPacketBuffer(); @@ -2055,6 +2057,7 @@ class TestPacketWriter : public QuicPacketWriter { QuicIpAddress last_write_source_address_; QuicSocketAddress last_write_peer_address_; int write_error_code_{0}; + QuicEcnCodepoint last_ecn_sent_ = ECN_NOT_ECT; }; // Parses a packet generated by diff --git a/net/url_request/url_request_test_job.cc b/net/url_request/url_request_test_job.cc index 4fc061225..10be229f5 100644 --- a/net/url_request/url_request_test_job.cc +++ b/net/url_request/url_request_test_job.cc @@ -287,7 +287,7 @@ void URLRequestTestJob::ProcessNextOperation() { stage_ = DATA_AVAILABLE; // OK if ReadRawData wasn't called yet. if (async_buf_) { - int result = CopyDataForRead(async_buf_, async_buf_size_); + int result = CopyDataForRead(async_buf_.get(), async_buf_size_); if (result < 0) NOTREACHED() << "Reads should not fail in DATA_AVAILABLE."; if (NextReadAsync()) { diff --git a/net/url_request/url_request_test_job.h b/net/url_request/url_request_test_job.h index d464244f2..a144685e9 100644 --- a/net/url_request/url_request_test_job.h +++ b/net/url_request/url_request_test_job.h @@ -173,7 +173,7 @@ class URLRequestTestJob : public URLRequestJob { int offset_ = 0; // Holds the buffer for an asynchronous ReadRawData call - raw_ptr<IOBuffer> async_buf_ = nullptr; + scoped_refptr<IOBuffer> async_buf_; int async_buf_size_ = 0; LoadTimingInfo load_timing_info_; diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 6b0807ffc..48cf06c06 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -10496,8 +10496,9 @@ class HTTPSCertNetFetchingTest : public HTTPSRequestTest { } void UpdateCertVerifier(scoped_refptr<CRLSet> crl_set) { - updatable_cert_verifier_->UpdateVerifyProcData(cert_net_fetcher_, - std::move(crl_set), nullptr); + net::CertVerifyProcFactory::ImplParams params; + params.crl_set = std::move(crl_set); + updatable_cert_verifier_->UpdateVerifyProcData(cert_net_fetcher_, params); } scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher_; @@ -11606,16 +11607,15 @@ TEST_F(HTTPSLocalCRLSetTest, KnownInterceptionBlocked) { // Configure a CRL that will mark |root_ca_cert| as a blocked interception // root. std::string crl_set_bytes; - scoped_refptr<CRLSet> crl_set; + net::CertVerifyProcFactory::ImplParams params; ASSERT_TRUE( base::ReadFileToString(GetTestCertsDirectory().AppendASCII( "crlset_blocked_interception_by_root.raw"), &crl_set_bytes)); - ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set)); + ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, ¶ms.crl_set)); updatable_cert_verifier_->UpdateVerifyProcData( - /*cert_net_fetcher=*/nullptr, crl_set, - /*root_store_data=*/nullptr); + /*cert_net_fetcher=*/nullptr, params); // Verify the connection fails as being a known interception root. { |