#include "net/socket/connect_job_params_factory.h"
#include <optional>
#include <variant>
#include <vector>
#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_util.h"
#include "net/base/features.h"
#include "net/base/host_port_pair.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/privacy_mode.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/base/request_priority.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/http_proxy_connect_job.h"
#include "net/socket/connect_job_params.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_tag.h"
#include "net/socket/socks_connect_job.h"
#include "net/socket/ssl_connect_job.h"
#include "net/socket/transport_connect_job.h"
#include "net/ssl/ssl_config.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
namespace net {
namespace {
void ConfigureAlpn(const ConnectJobFactory::Endpoint& endpoint,
ConnectJobFactory::AlpnMode alpn_mode,
const NetworkAnonymizationKey& network_anonymization_key,
const CommonConnectJobParams& common_connect_job_params,
SSLConfig& ssl_config,
bool renego_allowed) {
if (alpn_mode == ConnectJobFactory::AlpnMode::kDisabled) {
ssl_config.alpn_protos = {};
ssl_config.application_settings = {};
ssl_config.renego_allowed_default = false;
return;
}
DCHECK(std::holds_alternative<url::SchemeHostPort>(endpoint));
if (alpn_mode == ConnectJobFactory::AlpnMode::kHttp11Only) {
ssl_config.alpn_protos = {NextProto::kProtoHTTP11};
ssl_config.application_settings =
*common_connect_job_params.application_settings;
} else {
DCHECK_EQ(alpn_mode, ConnectJobFactory::AlpnMode::kHttpAll);
DCHECK(std::holds_alternative<url::SchemeHostPort>(endpoint));
ssl_config.alpn_protos = *common_connect_job_params.alpn_protos;
ssl_config.application_settings =
*common_connect_job_params.application_settings;
if (common_connect_job_params.http_server_properties) {
common_connect_job_params.http_server_properties->MaybeForceHTTP11(
std::get<url::SchemeHostPort>(endpoint), network_anonymization_key,
&ssl_config);
}
}
ssl_config.renego_allowed_default = renego_allowed;
if (renego_allowed) {
ssl_config.renego_allowed_for_protos = {NextProto::kProtoHTTP11};
}
}
base::flat_set<std::string> SupportedProtocolsFromSSLConfig(
const SSLConfig& config) {
return base::MakeFlatSet<std::string>(config.alpn_protos, {},
NextProtoToString);
}
HostPortPair ToHostPortPair(const ConnectJobFactory::Endpoint& endpoint) {
if (std::holds_alternative<url::SchemeHostPort>(endpoint)) {
return HostPortPair::FromSchemeHostPort(
std::get<url::SchemeHostPort>(endpoint));
}
DCHECK(
std::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
return std::get<ConnectJobFactory::SchemelessEndpoint>(endpoint)
.host_port_pair;
}
TransportSocketParams::Endpoint ToTransportEndpoint(
const ConnectJobFactory::Endpoint& endpoint) {
if (std::holds_alternative<url::SchemeHostPort>(endpoint)) {
return std::get<url::SchemeHostPort>(endpoint);
}
DCHECK(
std::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
return std::get<ConnectJobFactory::SchemelessEndpoint>(endpoint)
.host_port_pair;
}
bool UsingSsl(const ConnectJobFactory::Endpoint& endpoint) {
if (std::holds_alternative<url::SchemeHostPort>(endpoint)) {
return GURL::SchemeIsCryptographic(
base::ToLowerASCII(std::get<url::SchemeHostPort>(endpoint).scheme()));
}
DCHECK(
std::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
return std::get<ConnectJobFactory::SchemelessEndpoint>(endpoint).using_ssl;
}
ConnectJobParams MakeSSLSocketParams(
ConnectJobParams params,
const HostPortPair& host_and_port,
const SSLConfig& ssl_config,
const NetworkAnonymizationKey& network_anonymization_key) {
return ConnectJobParams(base::MakeRefCounted<SSLSocketParams>(
std::move(params), host_and_port, ssl_config, network_anonymization_key));
}
ConnectJobParams CreateProxyParams(
HostPortPair host_port_pair,
bool should_tunnel,
const ConnectJobFactory::Endpoint& endpoint,
const ProxyChain& proxy_chain,
size_t proxy_chain_index,
const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
const OnHostResolutionCallback& resolution_callback,
const NetworkAnonymizationKey& endpoint_network_anonymization_key,
SecureDnsPolicy secure_dns_policy,
const CommonConnectJobParams* common_connect_job_params,
const NetworkAnonymizationKey& proxy_dns_network_anonymization_key
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
bool secure_dns_only
#endif
) {
const ProxyServer& proxy_server =
proxy_chain.GetProxyServer(proxy_chain_index);
bool use_empty_nak = proxy_chain_index < proxy_chain.length() - 1;
const NetworkAnonymizationKey& network_anonymization_key =
use_empty_nak ? NetworkAnonymizationKey()
: endpoint_network_anonymization_key;
SSLConfig proxy_server_ssl_config;
if (proxy_server.is_secure_http_like()) {
proxy_server_ssl_config.disable_cert_verification_network_fetches = true;
ConfigureAlpn(url::SchemeHostPort(url::kHttpsScheme,
proxy_server.host_port_pair().host(),
proxy_server.host_port_pair().port()),
ConnectJobFactory::AlpnMode::kHttpAll,
network_anonymization_key, *common_connect_job_params,
proxy_server_ssl_config,
false);
proxy_server_ssl_config.proxy_chain = proxy_chain;
proxy_server_ssl_config.proxy_chain_index = proxy_chain_index;
proxy_server_ssl_config.session_usage = SessionUsage::kProxy;
}
ConnectJobParams params;
if (proxy_server.is_quic()) {
for (size_t i = 0; i < proxy_chain_index; i++) {
CHECK(proxy_chain.GetProxyServer(i).is_quic());
}
return ConnectJobParams(base::MakeRefCounted<HttpProxySocketParams>(
std::move(proxy_server_ssl_config), host_port_pair, proxy_chain,
proxy_chain_index, should_tunnel, *proxy_annotation_tag,
network_anonymization_key, secure_dns_policy));
} else if (proxy_chain_index == 0) {
params = ConnectJobParams(base::MakeRefCounted<TransportSocketParams>(
proxy_server.host_port_pair(), proxy_dns_network_anonymization_key,
secure_dns_policy, resolution_callback,
SupportedProtocolsFromSSLConfig(proxy_server_ssl_config)
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only
#endif
));
} else {
params = CreateProxyParams(
proxy_server.host_port_pair(), true, endpoint, proxy_chain,
proxy_chain_index - 1, proxy_annotation_tag, resolution_callback,
endpoint_network_anonymization_key, secure_dns_policy,
common_connect_job_params, proxy_dns_network_anonymization_key
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only
#endif
);
}
if (proxy_server.is_secure_http_like()) {
params =
MakeSSLSocketParams(std::move(params), proxy_server.host_port_pair(),
proxy_server_ssl_config, network_anonymization_key);
}
if (proxy_server.is_http_like()) {
CHECK(!proxy_server.is_quic());
params = ConnectJobParams(base::MakeRefCounted<HttpProxySocketParams>(
std::move(params), host_port_pair, proxy_chain, proxy_chain_index,
should_tunnel, *proxy_annotation_tag, network_anonymization_key,
secure_dns_policy));
} else {
DCHECK(proxy_server.is_socks());
DCHECK_EQ(1u, proxy_chain.length());
params = ConnectJobParams(base::MakeRefCounted<SOCKSSocketParams>(
std::move(params), proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5,
ToHostPortPair(endpoint), network_anonymization_key,
*proxy_annotation_tag));
}
return params;
}
}
ConnectJobParams ConstructConnectJobParams(
const ConnectJobFactory::Endpoint& endpoint,
const ProxyChain& proxy_chain,
const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
ConnectJobFactory::AlpnMode alpn_mode,
bool force_tunnel,
PrivacyMode privacy_mode,
const OnHostResolutionCallback& resolution_callback,
const NetworkAnonymizationKey& endpoint_network_anonymization_key,
SecureDnsPolicy secure_dns_policy,
bool disable_cert_network_fetches,
const CommonConnectJobParams* common_connect_job_params,
const NetworkAnonymizationKey& proxy_dns_network_anonymization_key
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
bool secure_dns_only
#endif
) {
DCHECK(proxy_chain.IsValid());
SSLConfig ssl_config;
if (UsingSsl(endpoint)) {
ssl_config.allowed_bad_certs = allowed_bad_certs;
ssl_config.privacy_mode = privacy_mode;
ConfigureAlpn(endpoint, alpn_mode, endpoint_network_anonymization_key,
*common_connect_job_params, ssl_config,
true);
ssl_config.disable_cert_verification_network_fetches =
disable_cert_network_fetches;
ssl_config.early_data_enabled =
*common_connect_job_params->enable_early_data;
ssl_config.proxy_chain = proxy_chain;
ssl_config.proxy_chain_index = proxy_chain.length();
ssl_config.session_usage = SessionUsage::kDestination;
}
ConnectJobParams params;
if (proxy_chain.is_direct()) {
params = ConnectJobParams(base::MakeRefCounted<TransportSocketParams>(
ToTransportEndpoint(endpoint), endpoint_network_anonymization_key,
secure_dns_policy, resolution_callback,
SupportedProtocolsFromSSLConfig(ssl_config)
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only
#endif
));
} else {
bool should_tunnel = force_tunnel || UsingSsl(endpoint) ||
!proxy_chain.is_get_to_proxy_allowed();
params = CreateProxyParams(
ToHostPortPair(endpoint), should_tunnel, endpoint, proxy_chain,
proxy_chain.length() - 1, proxy_annotation_tag,
resolution_callback, endpoint_network_anonymization_key,
secure_dns_policy, common_connect_job_params,
proxy_dns_network_anonymization_key
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only
#endif
);
}
if (UsingSsl(endpoint)) {
params =
MakeSSLSocketParams(std::move(params), ToHostPortPair(endpoint),
ssl_config, endpoint_network_anonymization_key);
}
return params;
}
}