chromium/chrome/browser/ash/crosapi/translate_proxy_config_to_crosapi.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/crosapi/network_settings_translation.h"

#include "base/strings/string_split.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/proxy_config/proxy_prefs.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_server.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/traffic_annotation/network_traffic_annotation.h"

namespace {

crosapi::mojom::ProxyLocation::Scheme NetSchemeToCrosapiScheme(
    net::ProxyServer::Scheme in) {
  switch (in) {
    case net::ProxyServer::Scheme::SCHEME_INVALID:
      return crosapi::mojom::ProxyLocation::Scheme::kInvalid;
    case net::ProxyServer::Scheme::SCHEME_HTTP:
      return crosapi::mojom::ProxyLocation::Scheme::kHttp;
    case net::ProxyServer::Scheme::SCHEME_SOCKS4:
      return crosapi::mojom::ProxyLocation::Scheme::kSocks4;
    case net::ProxyServer::Scheme::SCHEME_SOCKS5:
      return crosapi::mojom::ProxyLocation::Scheme::kSocks5;
    case net::ProxyServer::Scheme::SCHEME_HTTPS:
      return crosapi::mojom::ProxyLocation::Scheme::kHttps;
    case net::ProxyServer::Scheme::SCHEME_QUIC:
      return crosapi::mojom::ProxyLocation::Scheme::kQuic;
  }

  return crosapi::mojom::ProxyLocation::Scheme::kUnknown;
}

std::vector<crosapi::mojom::ProxyLocationPtr> TranslateProxyLocations(
    const net::ProxyList& proxy_list) {
  std::vector<crosapi::mojom::ProxyLocationPtr> proxy_ptr_list;
  for (const auto& proxy_chain : proxy_list.AllChains()) {
    // TODO(crbug.com/40284947): Remove single hop check when multi-hop proxy
    // chains are supported.
    CHECK(proxy_chain.is_single_proxy());
    net::ProxyServer proxy = proxy_chain.First();
    crosapi::mojom::ProxyLocationPtr proxy_ptr;
    proxy_ptr = crosapi::mojom::ProxyLocation::New();
    proxy_ptr->host = proxy.host_port_pair().host();
    proxy_ptr->port = proxy.host_port_pair().port();
    proxy_ptr->scheme = NetSchemeToCrosapiScheme(proxy.scheme());
    proxy_ptr_list.push_back(std::move(proxy_ptr));
  }
  return proxy_ptr_list;
}

crosapi::mojom::ProxySettingsManualPtr TranslateManualProxySettings(
    ProxyConfigDictionary* proxy_config) {
  crosapi::mojom::ProxySettingsManualPtr manual_proxy =
      crosapi::mojom::ProxySettingsManual::New();

  ProxyPrefs::ProxyMode mode;
  DCHECK(proxy_config->GetMode(&mode) &&
         mode == ProxyPrefs::MODE_FIXED_SERVERS);

  std::string proxy_servers;
  if (!proxy_config->GetProxyServer(&proxy_servers)) {
    LOG(ERROR) << "Missing manual proxy servers.";
    return nullptr;
  }

  net::ProxyConfig::ProxyRules rules;
  rules.ParseFromString(proxy_servers);

  switch (rules.type) {
    case net::ProxyConfig::ProxyRules::Type::EMPTY:
      return nullptr;
    case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
      if (!rules.single_proxies.IsEmpty()) {
        manual_proxy->http_proxies =
            TranslateProxyLocations(rules.single_proxies);
        manual_proxy->secure_http_proxies =
            TranslateProxyLocations(rules.single_proxies);
        manual_proxy->socks_proxies =
            TranslateProxyLocations(rules.single_proxies);
      }
      break;
    case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
      if (!rules.proxies_for_http.IsEmpty()) {
        manual_proxy->http_proxies =
            TranslateProxyLocations(rules.proxies_for_http);
      }
      if (!rules.proxies_for_https.IsEmpty()) {
        manual_proxy->secure_http_proxies =
            TranslateProxyLocations(rules.proxies_for_https);
      }
      if (!rules.fallback_proxies.IsEmpty()) {
        manual_proxy->socks_proxies =
            TranslateProxyLocations(rules.fallback_proxies);
      }
      break;
  }

  std::string bypass_list;
  if (proxy_config->GetBypassList(&bypass_list) && !bypass_list.empty()) {
    manual_proxy->exclude_domains = base::SplitString(
        bypass_list, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  }
  return manual_proxy;
}

}  // namespace

namespace crosapi {

// static
crosapi::mojom::ProxyConfigPtr ProxyConfigToCrosapiProxy(
    ProxyConfigDictionary* proxy_dict,
    GURL dhcp_wpad_url) {
  crosapi::mojom::ProxyConfigPtr proxy_config =
      crosapi::mojom::ProxyConfig::New();
  crosapi::mojom::ProxySettingsDirectPtr direct =
      crosapi::mojom::ProxySettingsDirect::New();

  ProxyPrefs::ProxyMode mode;
  if (!proxy_dict || !proxy_dict->GetMode(&mode)) {
    proxy_config->proxy_settings =
        crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
    return proxy_config;
  }
  switch (mode) {
    case ProxyPrefs::MODE_DIRECT:
      proxy_config->proxy_settings =
          crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
      break;
    case ProxyPrefs::MODE_AUTO_DETECT: {
      crosapi::mojom::ProxySettingsWpadPtr wpad =
          crosapi::mojom::ProxySettingsWpad::New();
      // WPAD with DHCP has a higher priority than DNS.
      if (dhcp_wpad_url.is_valid()) {
        wpad->pac_url = std::move(dhcp_wpad_url);
      } else {
        // Fallback to WPAD via DNS.
        wpad->pac_url = GURL("http://wpad/wpad.dat");
      }
      proxy_config->proxy_settings =
          crosapi::mojom::ProxySettings::NewWpad(std::move(wpad));
      break;
    }
    case ProxyPrefs::MODE_PAC_SCRIPT: {
      std::string pac_url;
      if (!proxy_dict->GetPacUrl(&pac_url)) {
        proxy_config->proxy_settings =
            crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
        LOG(ERROR) << "No pac URL for pac_script proxy mode.";
        break;
      }
      bool pac_mandatory = false;
      proxy_dict->GetPacMandatory(&pac_mandatory);

      crosapi::mojom::ProxySettingsPacPtr pac =
          crosapi::mojom::ProxySettingsPac::New();
      pac->pac_url = GURL(pac_url);
      pac->pac_mandatory = pac_mandatory;
      proxy_config->proxy_settings =
          crosapi::mojom::ProxySettings::NewPac(std::move(pac));
      break;
    }
    case ProxyPrefs::MODE_FIXED_SERVERS: {
      crosapi::mojom::ProxySettingsManualPtr manual =
          TranslateManualProxySettings(proxy_dict);
      proxy_config->proxy_settings =
          crosapi::mojom::ProxySettings::NewManual(std::move(manual));
      break;
    }
    case ProxyPrefs::MODE_SYSTEM:
      // This mode means Chrome is getting the settings from the operating
      // system. On Chrome OS, ash-chrome is the source of truth for proxy
      // settings so this mode is never used.
      NOTREACHED_IN_MIGRATION()
          << "The system mode doesn't apply to Ash-Chrome";
      break;
    default:
      LOG(ERROR) << "Incorrect proxy mode.";
      proxy_config->proxy_settings =
          crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
  }

  return proxy_config;
}

}  // namespace crosapi