chromium/net/proxy_resolution/configured_proxy_resolution_service.cc

// Copyright 2012 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/proxy_resolution/configured_proxy_resolution_service.h"

#include <algorithm>
#include <cmath>
#include <memory>
#include <utility>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "net/base/net_errors.h"
#include "net/base/net_info_source_list.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/proxy_delegate.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/url_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_util.h"
#include "net/log/net_log_with_source.h"
#include "net/proxy_resolution/configured_proxy_resolution_request.h"
#include "net/proxy_resolution/dhcp_pac_file_fetcher.h"
#include "net/proxy_resolution/multi_threaded_proxy_resolver.h"
#include "net/proxy_resolution/pac_file_decider.h"
#include "net/proxy_resolution/pac_file_fetcher.h"
#include "net/proxy_resolution/proxy_config_service_fixed.h"
#include "net/proxy_resolution/proxy_resolver_factory.h"
#include "net/url_request/url_request_context.h"

#if BUILDFLAG(IS_WIN)
#include "net/proxy_resolution/win/proxy_resolver_winhttp.h"
#elif BUILDFLAG(IS_APPLE)
#include "net/proxy_resolution/proxy_resolver_apple.h"
#endif

TimeTicks;

namespace net {

namespace {

const size_t kDefaultNumPacThreads =;

// When the IP address changes we don't immediately re-run proxy auto-config.
// Instead, we  wait for |kDelayAfterNetworkChangesMs| before
// attempting to re-valuate proxy auto-config.
//
// During this time window, any resolve requests sent to the
// ConfiguredProxyResolutionService will be queued. Once we have waited the
// required amount of them, the proxy auto-config step will be run, and the
// queued requests resumed.
//
// The reason we play this game is that our signal for detecting network
// changes (NetworkChangeNotifier) may fire *before* the system's networking
// dependencies are fully configured. This is a problem since it means if
// we were to run proxy auto-config right away, it could fail due to spurious
// DNS failures. (see http://crbug.com/50779 for more details.)
//
// By adding the wait window, we give things a better chance to get properly
// set up. Network failures can happen at any time though, so we additionally
// poll the PAC script for changes, which will allow us to recover from these
// sorts of problems.
const int64_t kDelayAfterNetworkChangesMs =;

// This is the default policy for polling the PAC script.
//
// In response to a failure, the poll intervals are:
//    0: 8 seconds  (scheduled on timer)
//    1: 32 seconds
//    2: 2 minutes
//    3+: 4 hours
//
// In response to a success, the poll intervals are:
//    0+: 12 hours
//
// Only the 8 second poll is scheduled on a timer, the rest happen in response
// to network activity (and hence will take longer than the written time).
//
// Explanation for these values:
//
// TODO(eroman): These values are somewhat arbitrary, and need to be tuned
// using some histograms data. Trying to be conservative so as not to break
// existing setups when deployed. A simple exponential retry scheme would be
// more elegant, but places more load on server.
//
// The motivation for trying quickly after failures (8 seconds) is to recover
// from spurious network failures, which are common after the IP address has
// just changed (like DNS failing to resolve). The next 32 second boundary is
// to try and catch other VPN weirdness which anecdotally I have seen take
// 10+ seconds for some users.
//
// The motivation for re-trying after a success is to check for possible
// content changes to the script, or to the WPAD auto-discovery results. We are
// not very aggressive with these checks so as to minimize the risk of
// overloading existing PAC setups. Moreover it is unlikely that PAC scripts
// change very frequently in existing setups. More research is needed to
// motivate what safe values are here, and what other user agents do.
//
// Comparison to other browsers:
//
// In Firefox the PAC URL is re-tried on failures according to
// network.proxy.autoconfig_retry_interval_min and
// network.proxy.autoconfig_retry_interval_max. The defaults are 5 seconds and
// 5 minutes respectively. It doubles the interval at each attempt.
//
// TODO(eroman): Figure out what Internet Explorer does.
class DefaultPollPolicy
    : public ConfiguredProxyResolutionService::PacPollPolicy {};

// Config getter that always returns direct settings.
class ProxyConfigServiceDirect : public ProxyConfigService {};

// Proxy resolver that fails every time.
class ProxyResolverNull : public ProxyResolver {};

// ProxyResolver that simulates a PAC script which returns
// |pac_string| for every single URL.
class ProxyResolverFromPacString : public ProxyResolver {};

// ProxyResolver that simulates a proxy chain which returns
// |proxy_chain| for every single URL.
class ProxyResolverFromProxyChains : public ProxyResolver {};

// Creates ProxyResolvers using a platform-specific implementation.
class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory {};

class ProxyResolverFactoryForNullResolver : public ProxyResolverFactory {};

class ProxyResolverFactoryForPacResult : public ProxyResolverFactory {};

class ProxyResolverFactoryForProxyChains : public ProxyResolverFactory {};

// Returns NetLog parameters describing a proxy configuration change.
base::Value::Dict NetLogProxyConfigChangedParams(
    const std::optional<ProxyConfigWithAnnotation>* old_config,
    const ProxyConfigWithAnnotation* new_config) {}

base::Value::Dict NetLogBadProxyListParams(
    const ProxyRetryInfoMap* retry_info) {}

// Returns NetLog parameters on a successful proxy resolution.
base::Value::Dict NetLogFinishedResolvingProxyParams(const ProxyInfo* result) {}

// Returns a sanitized copy of |url| which is safe to pass on to a PAC script.
//
// PAC scripts are modelled as being controllable by a network-present
// attacker (since such an attacker can influence the outcome of proxy
// auto-discovery, or modify the contents of insecurely delivered PAC scripts).
//
// As such, it is important that the full path/query of https:// URLs not be
// sent to PAC scripts, since that would give an attacker access to data that
// is ordinarily protected by TLS.
//
// Obscuring the path for http:// URLs isn't being done since it doesn't matter
// for security (attacker can already route traffic through their HTTP proxy
// and see the full URL for http:// requests).
//
// TODO(crbug.com/41412888): Use the same stripping for insecure URL
// schemes.
GURL SanitizeUrl(const GURL& url) {}

}  // namespace

// ConfiguredProxyResolutionService::InitProxyResolver
// ----------------------------------

// This glues together two asynchronous steps:
//   (1) PacFileDecider -- try to fetch/validate a sequence of PAC scripts
//       to figure out what we should configure against.
//   (2) Feed the fetched PAC script into the ProxyResolver.
//
// InitProxyResolver is a single-use class which encapsulates cancellation as
// part of its destructor. Start() or StartSkipDecider() should be called just
// once. The instance can be destroyed at any time, and the request will be
// cancelled.

class ConfiguredProxyResolutionService::InitProxyResolver {};

// ConfiguredProxyResolutionService::PacFileDeciderPoller
// ---------------------------

// This helper class encapsulates the logic to schedule and run periodic
// background checks to see if the PAC script (or effective proxy configuration)
// has changed. If a change is detected, then the caller will be notified via
// the ChangeCallback.
class ConfiguredProxyResolutionService::PacFileDeciderPoller {};

// static
const ConfiguredProxyResolutionService::PacPollPolicy*
    ConfiguredProxyResolutionService::PacFileDeciderPoller::poll_policy_ =;

// ConfiguredProxyResolutionService
// -----------------------------------------------------

ConfiguredProxyResolutionService::ConfiguredProxyResolutionService(
    std::unique_ptr<ProxyConfigService> config_service,
    std::unique_ptr<ProxyResolverFactory> resolver_factory,
    NetLog* net_log,
    bool quick_check_enabled)
    :{}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
    std::unique_ptr<ProxyConfigService> proxy_config_service,
    NetLog* net_log,
    bool quick_check_enabled) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
    std::unique_ptr<ProxyConfigService> proxy_config_service,
    NetLog* net_log) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateFixedForTest(
    const ProxyConfigWithAnnotation& pc) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateFixedForTest(
    const std::string& proxy,
    const NetworkTrafficAnnotationTag& traffic_annotation) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateDirect() {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
    const std::string& pac_string,
    const NetworkTrafficAnnotationTag& traffic_annotation) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateFixedFromAutoDetectedPacResultForTest(
    const std::string& pac_string,
    const NetworkTrafficAnnotationTag& traffic_annotation) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService>
ConfiguredProxyResolutionService::CreateFixedFromProxyChainsForTest(
    const std::vector<ProxyChain>& proxy_chains,
    const NetworkTrafficAnnotationTag& traffic_annotation) {}

int ConfiguredProxyResolutionService::ResolveProxy(
    const GURL& raw_url,
    const std::string& method,
    const NetworkAnonymizationKey& network_anonymization_key,
    ProxyInfo* result,
    CompletionOnceCallback callback,
    std::unique_ptr<ProxyResolutionRequest>* out_request,
    const NetLogWithSource& net_log) {}

int ConfiguredProxyResolutionService::TryToCompleteSynchronously(
    const GURL& url,
    ProxyInfo* result) {}

ConfiguredProxyResolutionService::~ConfiguredProxyResolutionService() {}

void ConfiguredProxyResolutionService::SuspendAllPendingRequests() {}

void ConfiguredProxyResolutionService::SetReady() {}

void ConfiguredProxyResolutionService::ApplyProxyConfigIfAvailable() {}

void ConfiguredProxyResolutionService::OnInitProxyResolverComplete(int result) {}

void ConfiguredProxyResolutionService::ReportSuccess(const ProxyInfo& result) {}

bool ConfiguredProxyResolutionService::ContainsPendingRequest(
    ConfiguredProxyResolutionRequest* req) {}

void ConfiguredProxyResolutionService::RemovePendingRequest(
    ConfiguredProxyResolutionRequest* req) {}

int ConfiguredProxyResolutionService::DidFinishResolvingProxy(
    const GURL& url,
    const NetworkAnonymizationKey& network_anonymization_key,
    const std::string& method,
    ProxyInfo* result,
    int result_code,
    const NetLogWithSource& net_log) {}

void ConfiguredProxyResolutionService::SetPacFileFetchers(
    std::unique_ptr<PacFileFetcher> pac_file_fetcher,
    std::unique_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher) {}

void ConfiguredProxyResolutionService::SetProxyDelegate(
    ProxyDelegate* delegate) {}

void ConfiguredProxyResolutionService::OnShutdown() {}

const ProxyRetryInfoMap& ConfiguredProxyResolutionService::proxy_retry_info()
    const {}

void ConfiguredProxyResolutionService::ClearBadProxiesCache() {}

PacFileFetcher* ConfiguredProxyResolutionService::GetPacFileFetcher() const {}

bool ConfiguredProxyResolutionService::GetLoadStateIfAvailable(
    LoadState* load_state) const {}

ProxyResolver* ConfiguredProxyResolutionService::GetProxyResolver() const {}

ConfiguredProxyResolutionService::State
ConfiguredProxyResolutionService::ResetProxyConfig(bool reset_fetched_config) {}

void ConfiguredProxyResolutionService::ForceReloadProxyConfig() {}

base::Value::Dict ConfiguredProxyResolutionService::GetProxyNetLogValues() {}

bool ConfiguredProxyResolutionService::CastToConfiguredProxyResolutionService(
    ConfiguredProxyResolutionService** configured_proxy_resolution_service) {}

// static
const ConfiguredProxyResolutionService::PacPollPolicy*
ConfiguredProxyResolutionService::set_pac_script_poll_policy(
    const PacPollPolicy* policy) {}

// static
std::unique_ptr<ConfiguredProxyResolutionService::PacPollPolicy>
ConfiguredProxyResolutionService::CreateDefaultPacPollPolicy() {}

void ConfiguredProxyResolutionService::OnProxyConfigChanged(
    const ProxyConfigWithAnnotation& config,
    ProxyConfigService::ConfigAvailability availability) {}

bool ConfiguredProxyResolutionService::ApplyPacBypassRules(const GURL& url,
                                                           ProxyInfo* results) {}

void ConfiguredProxyResolutionService::InitializeUsingLastFetchedConfig() {}

void ConfiguredProxyResolutionService::InitializeUsingDecidedConfig(
    int decider_result,
    const PacFileDataWithSource& script_data,
    const ProxyConfigWithAnnotation& effective_config) {}

void ConfiguredProxyResolutionService::OnIPAddressChanged() {}

void ConfiguredProxyResolutionService::OnDNSChanged() {}

}  // namespace net