#include "net/http/http_response_info.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/pickle.h"
#include "base/time/time.h"
#include "net/base/net_errors.h"
#include "net/cert/sct_status_flags.h"
#include "net/cert/signed_certificate_timestamp.h"
#include "net/cert/x509_certificate.h"
#include "net/http/http_response_headers.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
using base::Time;
namespace net {
namespace {
bool KeyExchangeGroupIsValid(int ssl_connection_status) {
if (SSLConnectionStatusToVersion(ssl_connection_status) >=
SSL_CONNECTION_VERSION_TLS1_3) {
return true;
}
const SSL_CIPHER* cipher = SSL_get_cipher_by_value(
SSLConnectionStatusToCipherSuite(ssl_connection_status));
return cipher && SSL_CIPHER_get_kx_nid(cipher) == NID_kx_ecdhe;
}
}
enum {
RESPONSE_INFO_VERSION = 3,
RESPONSE_INFO_MINIMUM_VERSION = 3,
RESPONSE_INFO_VERSION_MASK = 0xFF,
RESPONSE_INFO_HAS_CERT = 1 << 8,
RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9,
RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10,
RESPONSE_INFO_HAS_VARY_DATA = 1 << 11,
RESPONSE_INFO_TRUNCATED = 1 << 12,
RESPONSE_INFO_WAS_SPDY = 1 << 13,
RESPONSE_INFO_WAS_ALPN = 1 << 14,
RESPONSE_INFO_WAS_PROXY = 1 << 15,
RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16,
RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL = 1 << 17,
RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18,
RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19,
RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS = 1 << 20,
RESPONSE_INFO_UNUSED_SINCE_PREFETCH = 1 << 21,
RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP = 1 << 22,
RESPONSE_INFO_PKP_BYPASSED = 1 << 23,
RESPONSE_INFO_HAS_STALENESS = 1 << 24,
RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM = 1 << 25,
RESPONSE_INFO_RESTRICTED_PREFETCH = 1 << 26,
RESPONSE_INFO_HAS_DNS_ALIASES = 1 << 27,
RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE = 1 << 28,
RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO = 1 << 29,
RESPONSE_INFO_BROWSER_RUN_ID = 1 << 30,
};
HttpResponseInfo::ConnectionInfoCoarse HttpResponseInfo::ConnectionInfoToCoarse(
ConnectionInfo info) {
switch (info) {
case CONNECTION_INFO_HTTP0_9:
case CONNECTION_INFO_HTTP1_0:
case CONNECTION_INFO_HTTP1_1:
return CONNECTION_INFO_COARSE_HTTP1;
case CONNECTION_INFO_HTTP2:
case CONNECTION_INFO_DEPRECATED_SPDY2:
case CONNECTION_INFO_DEPRECATED_SPDY3:
case CONNECTION_INFO_DEPRECATED_HTTP2_14:
case CONNECTION_INFO_DEPRECATED_HTTP2_15:
return CONNECTION_INFO_COARSE_HTTP2;
case CONNECTION_INFO_QUIC_UNKNOWN_VERSION:
case CONNECTION_INFO_QUIC_32:
case CONNECTION_INFO_QUIC_33:
case CONNECTION_INFO_QUIC_34:
case CONNECTION_INFO_QUIC_35:
case CONNECTION_INFO_QUIC_36:
case CONNECTION_INFO_QUIC_37:
case CONNECTION_INFO_QUIC_38:
case CONNECTION_INFO_QUIC_39:
case CONNECTION_INFO_QUIC_40:
case CONNECTION_INFO_QUIC_41:
case CONNECTION_INFO_QUIC_42:
case CONNECTION_INFO_QUIC_43:
case CONNECTION_INFO_QUIC_44:
case CONNECTION_INFO_QUIC_45:
case CONNECTION_INFO_QUIC_46:
case CONNECTION_INFO_QUIC_47:
case CONNECTION_INFO_QUIC_Q048:
case CONNECTION_INFO_QUIC_T048:
case CONNECTION_INFO_QUIC_Q049:
case CONNECTION_INFO_QUIC_T049:
case CONNECTION_INFO_QUIC_Q050:
case CONNECTION_INFO_QUIC_T050:
case CONNECTION_INFO_QUIC_Q099:
case CONNECTION_INFO_QUIC_T099:
case CONNECTION_INFO_QUIC_999:
case CONNECTION_INFO_QUIC_DRAFT_25:
case CONNECTION_INFO_QUIC_DRAFT_27:
case CONNECTION_INFO_QUIC_DRAFT_28:
case CONNECTION_INFO_QUIC_DRAFT_29:
case CONNECTION_INFO_QUIC_T051:
case CONNECTION_INFO_QUIC_RFC_V1:
case CONNECTION_INFO_DEPRECATED_QUIC_2_DRAFT_1:
case CONNECTION_INFO_QUIC_2_DRAFT_8:
return CONNECTION_INFO_COARSE_QUIC;
case CONNECTION_INFO_UNKNOWN:
return CONNECTION_INFO_COARSE_OTHER;
case NUM_OF_CONNECTION_INFOS:
NOTREACHED();
return CONNECTION_INFO_COARSE_OTHER;
}
NOTREACHED();
return CONNECTION_INFO_COARSE_OTHER;
}
HttpResponseInfo::HttpResponseInfo() = default;
HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) = default;
HttpResponseInfo::~HttpResponseInfo() = default;
HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) =
default;
bool HttpResponseInfo::InitFromPickle(const base::Pickle& pickle,
bool* response_truncated) {
base::PickleIterator iter(pickle);
int flags;
if (!iter.ReadInt(&flags))
return false;
int version = flags & RESPONSE_INFO_VERSION_MASK;
if (version < RESPONSE_INFO_MINIMUM_VERSION ||
version > RESPONSE_INFO_VERSION) {
DLOG(ERROR) << "unexpected response info version: " << version;
return false;
}
int64_t time_val;
if (!iter.ReadInt64(&time_val))
return false;
request_time = Time::FromInternalValue(time_val);
was_cached = true;
if (!iter.ReadInt64(&time_val))
return false;
response_time = Time::FromInternalValue(time_val);
headers = base::MakeRefCounted<HttpResponseHeaders>(&iter);
if (headers->response_code() == -1)
return false;
if (flags & RESPONSE_INFO_HAS_CERT) {
ssl_info.cert = X509Certificate::CreateFromPickle(&iter);
if (!ssl_info.cert.get())
return false;
}
if (flags & RESPONSE_INFO_HAS_CERT_STATUS) {
CertStatus cert_status;
if (!iter.ReadUInt32(&cert_status))
return false;
ssl_info.cert_status = cert_status;
}
if (flags & RESPONSE_INFO_HAS_SECURITY_BITS) {
int security_bits;
if (!iter.ReadInt(&security_bits))
return false;
}
if (flags & RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS) {
int connection_status;
if (!iter.ReadInt(&connection_status))
return false;
if (SSLConnectionStatusToVersion(connection_status) ==
SSL_CONNECTION_VERSION_SSL3) {
return false;
}
ssl_info.connection_status = connection_status;
}
if (flags & RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS) {
int num_scts;
if (!iter.ReadInt(&num_scts))
return false;
for (int i = 0; i < num_scts; ++i) {
scoped_refptr<ct::SignedCertificateTimestamp> sct(
ct::SignedCertificateTimestamp::CreateFromPickle(&iter));
uint16_t status;
if (!sct.get() || !iter.ReadUInt16(&status))
return false;
}
}
if (flags & RESPONSE_INFO_HAS_VARY_DATA) {
if (!vary_data.InitFromPickle(&iter))
return false;
}
std::string socket_address_host;
if (!iter.ReadString(&socket_address_host))
return false;
uint16_t socket_address_port;
if (!iter.ReadUInt16(&socket_address_port))
return false;
IPAddress ip_address;
if (ip_address.AssignFromIPLiteral(socket_address_host)) {
remote_endpoint = IPEndPoint(ip_address, socket_address_port);
} else if (ParseURLHostnameToAddress(socket_address_host, &ip_address)) {
remote_endpoint = IPEndPoint(ip_address, socket_address_port);
}
if (flags & RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL) {
if (!iter.ReadString(&alpn_negotiated_protocol))
return false;
}
if (flags & RESPONSE_INFO_HAS_CONNECTION_INFO) {
int value;
if (!iter.ReadInt(&value))
return false;
if (value > static_cast<int>(CONNECTION_INFO_UNKNOWN) &&
value < static_cast<int>(NUM_OF_CONNECTION_INFOS)) {
connection_info = static_cast<ConnectionInfo>(value);
}
}
if (flags & RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP) {
int key_exchange_group;
if (!iter.ReadInt(&key_exchange_group))
return false;
if (KeyExchangeGroupIsValid(ssl_info.connection_status))
ssl_info.key_exchange_group = key_exchange_group;
}
if (flags & RESPONSE_INFO_HAS_STALENESS) {
if (!iter.ReadInt64(&time_val))
return false;
stale_revalidate_timeout = base::Time() + base::Microseconds(time_val);
}
was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0;
was_alpn_negotiated = (flags & RESPONSE_INFO_WAS_ALPN) != 0;
was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0;
*response_truncated = (flags & RESPONSE_INFO_TRUNCATED) != 0;
did_use_http_auth = (flags & RESPONSE_INFO_USE_HTTP_AUTHENTICATION) != 0;
unused_since_prefetch = (flags & RESPONSE_INFO_UNUSED_SINCE_PREFETCH) != 0;
restricted_prefetch = (flags & RESPONSE_INFO_RESTRICTED_PREFETCH) != 0;
single_keyed_cache_entry_unusable =
(flags & RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE) != 0;
ssl_info.pkp_bypassed = (flags & RESPONSE_INFO_PKP_BYPASSED) != 0;
if (flags & RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM) {
int peer_signature_algorithm;
if (!iter.ReadInt(&peer_signature_algorithm) ||
!base::IsValueInRangeForNumericType<uint16_t>(
peer_signature_algorithm)) {
return false;
}
ssl_info.peer_signature_algorithm =
base::checked_cast<uint16_t>(peer_signature_algorithm);
}
if (flags & RESPONSE_INFO_HAS_DNS_ALIASES) {
int num_aliases;
if (!iter.ReadInt(&num_aliases))
return false;
std::string alias;
for (int i = 0; i < num_aliases; i++) {
if (!iter.ReadString(&alias))
return false;
dns_aliases.insert(alias);
}
}
ssl_info.encrypted_client_hello =
(flags & RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO) != 0;
if (flags & RESPONSE_INFO_BROWSER_RUN_ID) {
int64_t id;
if (!iter.ReadInt64(&id))
return false;
browser_run_id = absl::make_optional(id);
}
return true;
}
void HttpResponseInfo::Persist(base::Pickle* pickle,
bool skip_transient_headers,
bool response_truncated) const {
int flags = RESPONSE_INFO_VERSION;
if (ssl_info.is_valid()) {
flags |= RESPONSE_INFO_HAS_CERT;
flags |= RESPONSE_INFO_HAS_CERT_STATUS;
if (ssl_info.key_exchange_group != 0)
flags |= RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP;
if (ssl_info.connection_status != 0)
flags |= RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS;
if (ssl_info.peer_signature_algorithm != 0)
flags |= RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM;
}
if (vary_data.is_valid())
flags |= RESPONSE_INFO_HAS_VARY_DATA;
if (response_truncated)
flags |= RESPONSE_INFO_TRUNCATED;
if (was_fetched_via_spdy)
flags |= RESPONSE_INFO_WAS_SPDY;
if (was_alpn_negotiated) {
flags |= RESPONSE_INFO_WAS_ALPN;
flags |= RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL;
}
if (was_fetched_via_proxy)
flags |= RESPONSE_INFO_WAS_PROXY;
if (connection_info != CONNECTION_INFO_UNKNOWN)
flags |= RESPONSE_INFO_HAS_CONNECTION_INFO;
if (did_use_http_auth)
flags |= RESPONSE_INFO_USE_HTTP_AUTHENTICATION;
if (unused_since_prefetch)
flags |= RESPONSE_INFO_UNUSED_SINCE_PREFETCH;
if (restricted_prefetch)
flags |= RESPONSE_INFO_RESTRICTED_PREFETCH;
if (single_keyed_cache_entry_unusable)
flags |= RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE;
if (ssl_info.pkp_bypassed)
flags |= RESPONSE_INFO_PKP_BYPASSED;
if (!stale_revalidate_timeout.is_null())
flags |= RESPONSE_INFO_HAS_STALENESS;
if (!dns_aliases.empty())
flags |= RESPONSE_INFO_HAS_DNS_ALIASES;
if (ssl_info.encrypted_client_hello)
flags |= RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO;
if (browser_run_id.has_value())
flags |= RESPONSE_INFO_BROWSER_RUN_ID;
pickle->WriteInt(flags);
pickle->WriteInt64(request_time.ToInternalValue());
pickle->WriteInt64(response_time.ToInternalValue());
HttpResponseHeaders::PersistOptions persist_options =
HttpResponseHeaders::PERSIST_RAW;
if (skip_transient_headers) {
persist_options = HttpResponseHeaders::PERSIST_SANS_COOKIES |
HttpResponseHeaders::PERSIST_SANS_CHALLENGES |
HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP |
HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
HttpResponseHeaders::PERSIST_SANS_RANGES |
HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE;
}
headers->Persist(pickle, persist_options);
if (ssl_info.is_valid()) {
ssl_info.cert->Persist(pickle);
pickle->WriteUInt32(ssl_info.cert_status);
if (ssl_info.connection_status != 0)
pickle->WriteInt(ssl_info.connection_status);
}
if (vary_data.is_valid())
vary_data.Persist(pickle);
pickle->WriteString(remote_endpoint.ToStringWithoutPort());
pickle->WriteUInt16(remote_endpoint.port());
if (was_alpn_negotiated)
pickle->WriteString(alpn_negotiated_protocol);
if (connection_info != CONNECTION_INFO_UNKNOWN)
pickle->WriteInt(static_cast<int>(connection_info));
if (ssl_info.is_valid() && ssl_info.key_exchange_group != 0)
pickle->WriteInt(ssl_info.key_exchange_group);
if (flags & RESPONSE_INFO_HAS_STALENESS) {
pickle->WriteInt64(
(stale_revalidate_timeout - base::Time()).InMicroseconds());
}
if (ssl_info.is_valid() && ssl_info.peer_signature_algorithm != 0)
pickle->WriteInt(ssl_info.peer_signature_algorithm);
if (!dns_aliases.empty()) {
pickle->WriteInt(dns_aliases.size());
for (const auto& alias : dns_aliases)
pickle->WriteString(alias);
}
if (browser_run_id.has_value()) {
pickle->WriteInt64(browser_run_id.value());
}
}
bool HttpResponseInfo::DidUseQuic() const {
switch (connection_info) {
case CONNECTION_INFO_UNKNOWN:
case CONNECTION_INFO_HTTP1_1:
case CONNECTION_INFO_DEPRECATED_SPDY2:
case CONNECTION_INFO_DEPRECATED_SPDY3:
case CONNECTION_INFO_HTTP2:
case CONNECTION_INFO_DEPRECATED_HTTP2_14:
case CONNECTION_INFO_DEPRECATED_HTTP2_15:
case CONNECTION_INFO_HTTP0_9:
case CONNECTION_INFO_HTTP1_0:
return false;
case CONNECTION_INFO_QUIC_UNKNOWN_VERSION:
case CONNECTION_INFO_QUIC_32:
case CONNECTION_INFO_QUIC_33:
case CONNECTION_INFO_QUIC_34:
case CONNECTION_INFO_QUIC_35:
case CONNECTION_INFO_QUIC_36:
case CONNECTION_INFO_QUIC_37:
case CONNECTION_INFO_QUIC_38:
case CONNECTION_INFO_QUIC_39:
case CONNECTION_INFO_QUIC_40:
case CONNECTION_INFO_QUIC_41:
case CONNECTION_INFO_QUIC_42:
case CONNECTION_INFO_QUIC_43:
case CONNECTION_INFO_QUIC_44:
case CONNECTION_INFO_QUIC_45:
case CONNECTION_INFO_QUIC_46:
case CONNECTION_INFO_QUIC_47:
case CONNECTION_INFO_QUIC_Q048:
case CONNECTION_INFO_QUIC_T048:
case CONNECTION_INFO_QUIC_Q049:
case CONNECTION_INFO_QUIC_T049:
case CONNECTION_INFO_QUIC_Q050:
case CONNECTION_INFO_QUIC_T050:
case CONNECTION_INFO_QUIC_Q099:
case CONNECTION_INFO_QUIC_T099:
case CONNECTION_INFO_QUIC_999:
case CONNECTION_INFO_QUIC_DRAFT_25:
case CONNECTION_INFO_QUIC_DRAFT_27:
case CONNECTION_INFO_QUIC_DRAFT_28:
case CONNECTION_INFO_QUIC_DRAFT_29:
case CONNECTION_INFO_QUIC_T051:
case CONNECTION_INFO_QUIC_RFC_V1:
case CONNECTION_INFO_DEPRECATED_QUIC_2_DRAFT_1:
case CONNECTION_INFO_QUIC_2_DRAFT_8:
return true;
case NUM_OF_CONNECTION_INFOS:
NOTREACHED();
return false;
}
NOTREACHED();
return false;
}
std::string HttpResponseInfo::ConnectionInfoToString(
ConnectionInfo connection_info) {
switch (connection_info) {
case CONNECTION_INFO_UNKNOWN:
return "unknown";
case CONNECTION_INFO_HTTP1_1:
return "http/1.1";
case CONNECTION_INFO_DEPRECATED_SPDY2:
NOTREACHED();
return "";
case CONNECTION_INFO_DEPRECATED_SPDY3:
return "spdy/3";
case CONNECTION_INFO_DEPRECATED_HTTP2_14:
case CONNECTION_INFO_DEPRECATED_HTTP2_15:
case CONNECTION_INFO_HTTP2:
return "h2";
case CONNECTION_INFO_QUIC_UNKNOWN_VERSION:
return "http/2+quic";
case CONNECTION_INFO_QUIC_32:
return "http/2+quic/32";
case CONNECTION_INFO_QUIC_33:
return "http/2+quic/33";
case CONNECTION_INFO_QUIC_34:
return "http/2+quic/34";
case CONNECTION_INFO_QUIC_35:
return "http/2+quic/35";
case CONNECTION_INFO_QUIC_36:
return "http/2+quic/36";
case CONNECTION_INFO_QUIC_37:
return "http/2+quic/37";
case CONNECTION_INFO_QUIC_38:
return "http/2+quic/38";
case CONNECTION_INFO_QUIC_39:
return "http/2+quic/39";
case CONNECTION_INFO_QUIC_40:
return "http/2+quic/40";
case CONNECTION_INFO_QUIC_41:
return "http/2+quic/41";
case CONNECTION_INFO_QUIC_42:
return "http/2+quic/42";
case CONNECTION_INFO_QUIC_43:
return "http/2+quic/43";
case CONNECTION_INFO_QUIC_44:
return "http/2+quic/44";
case CONNECTION_INFO_QUIC_45:
return "http/2+quic/45";
case CONNECTION_INFO_QUIC_46:
return "http/2+quic/46";
case CONNECTION_INFO_QUIC_47:
return "http/2+quic/47";
case CONNECTION_INFO_QUIC_Q048:
return "h3-Q048";
case CONNECTION_INFO_QUIC_T048:
return "h3-T048";
case CONNECTION_INFO_QUIC_Q049:
return "h3-Q049";
case CONNECTION_INFO_QUIC_T049:
return "h3-T049";
case CONNECTION_INFO_QUIC_Q050:
return "h3-Q050";
case CONNECTION_INFO_QUIC_T050:
return "h3-T050";
case CONNECTION_INFO_QUIC_Q099:
return "h3-Q099";
case CONNECTION_INFO_QUIC_DRAFT_25:
return "h3-25";
case CONNECTION_INFO_QUIC_DRAFT_27:
return "h3-27";
case CONNECTION_INFO_QUIC_DRAFT_28:
return "h3-28";
case CONNECTION_INFO_QUIC_DRAFT_29:
return "h3-29";
case CONNECTION_INFO_QUIC_T099:
return "h3-T099";
case CONNECTION_INFO_HTTP0_9:
return "http/0.9";
case CONNECTION_INFO_HTTP1_0:
return "http/1.0";
case CONNECTION_INFO_QUIC_999:
return "http2+quic/999";
case CONNECTION_INFO_QUIC_T051:
return "h3-T051";
case CONNECTION_INFO_QUIC_RFC_V1:
return "h3";
case CONNECTION_INFO_DEPRECATED_QUIC_2_DRAFT_1:
return "h3/quic2draft01";
case CONNECTION_INFO_QUIC_2_DRAFT_8:
return "h3/quic2draft08";
case NUM_OF_CONNECTION_INFOS:
break;
}
NOTREACHED();
return "";
}
}