chromium/chromeos/ash/components/sync_wifi/network_type_conversions.cc

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

#include "chromeos/ash/components/sync_wifi/network_type_conversions.h"

#include "base/strings/string_number_conversions.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "components/device_event_log/device_event_log.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash::network_config {
namespace mojom = ::chromeos::network_config::mojom;
}

namespace ash::sync_wifi {

namespace {

bool IsAutoconnectEnabled(
    sync_pb::WifiConfigurationSpecifics::AutomaticallyConnectOption
        auto_connect) {
  switch (auto_connect) {
    case sync_pb::WifiConfigurationSpecifics::AUTOMATICALLY_CONNECT_DISABLED:
      return false;

    case sync_pb::WifiConfigurationSpecifics::AUTOMATICALLY_CONNECT_ENABLED:
    case sync_pb::WifiConfigurationSpecifics::AUTOMATICALLY_CONNECT_UNSPECIFIED:
      return true;
  }
}

}  // namespace

// Returns an empty string when |base_16| is unable to be decoded from a
// hex string to bytes. This may signify an improperly encoded SSID.
std::string DecodeHexString(const std::string& base_16) {
  std::string decoded;
  DCHECK_EQ(base_16.size() % 2, 0u) << "Must be a multiple of 2";
  decoded.reserve(base_16.size() / 2);

  std::vector<uint8_t> v;
  if (!base::HexStringToBytes(base_16, &v)) {
    NET_LOG(EVENT) << "Failed to decode hex encoded SSID.";
    return std::string();
  }

  decoded.assign(reinterpret_cast<const char*>(v.data()), v.size());
  return decoded;
}

std::string SecurityTypeStringFromMojo(
    const network_config::mojom::SecurityType& security_type) {
  switch (security_type) {
    case network_config::mojom::SecurityType::kWpaPsk:
      return shill::kSecurityClassPsk;
    case network_config::mojom::SecurityType::kWepPsk:
      return shill::kSecurityClassWep;
    default:
      // Only PSK and WEP secured networks are supported by sync.
      return "";
  }
}

std::string SecurityTypeStringFromProto(
    const sync_pb::WifiConfigurationSpecifics_SecurityType& security_type) {
  switch (security_type) {
    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_PSK:
      return shill::kSecurityClassPsk;
    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_WEP:
      return shill::kSecurityClassWep;
    default:
      // Only PSK and WEP secured networks are supported by sync.
      NOTREACHED_IN_MIGRATION();
      return "";
  }
}

sync_pb::WifiConfigurationSpecifics_SecurityType SecurityTypeProtoFromMojo(
    const network_config::mojom::SecurityType& security_type) {
  switch (security_type) {
    case network_config::mojom::SecurityType::kWpaPsk:
      return sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_PSK;
    case network_config::mojom::SecurityType::kWepPsk:
      return sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_WEP;
    default:
      // Only PSK and WEP secured networks are supported by sync.
      NOTREACHED_IN_MIGRATION();
      return sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_NONE;
  }
}

sync_pb::WifiConfigurationSpecifics_AutomaticallyConnectOption
AutomaticallyConnectProtoFromMojo(
    const network_config::mojom::ManagedBooleanPtr& auto_connect) {
  if (!auto_connect) {
    return sync_pb::WifiConfigurationSpecifics::
        AUTOMATICALLY_CONNECT_UNSPECIFIED;
  }

  if (auto_connect->active_value) {
    return sync_pb::WifiConfigurationSpecifics::AUTOMATICALLY_CONNECT_ENABLED;
  }

  return sync_pb::WifiConfigurationSpecifics::AUTOMATICALLY_CONNECT_DISABLED;
}

sync_pb::WifiConfigurationSpecifics_IsPreferredOption IsPreferredProtoFromMojo(
    const network_config::mojom::ManagedInt32Ptr& is_preferred) {
  if (!is_preferred) {
    return sync_pb::WifiConfigurationSpecifics::IS_PREFERRED_UNSPECIFIED;
  }

  if (is_preferred->active_value == 1) {
    return sync_pb::WifiConfigurationSpecifics::IS_PREFERRED_ENABLED;
  }

  return sync_pb::WifiConfigurationSpecifics::IS_PREFERRED_DISABLED;
}

sync_pb::WifiConfigurationSpecifics_ProxyConfiguration_ProxyOption
ProxyOptionProtoFromMojo(
    const network_config::mojom::ManagedProxySettingsPtr& proxy_settings,
    bool is_unspecified) {
  if (!proxy_settings || is_unspecified) {
    return sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_UNSPECIFIED;
  }

  if (proxy_settings->type->active_value == ::onc::proxy::kPAC) {
    return sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_AUTOMATIC;
  }

  if (proxy_settings->type->active_value == ::onc::proxy::kWPAD) {
    return sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_AUTODISCOVERY;
  }

  if (proxy_settings->type->active_value == ::onc::proxy::kManual) {
    return sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_MANUAL;
  }

  return sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
      PROXY_OPTION_DISABLED;
}

sync_pb::WifiConfigurationSpecifics_ProxyConfiguration
ProxyConfigurationProtoFromMojo(
    const network_config::mojom::ManagedProxySettingsPtr& proxy_settings,
    bool is_unspecified) {
  sync_pb::WifiConfigurationSpecifics_ProxyConfiguration proto;
  proto.set_proxy_option(
      ProxyOptionProtoFromMojo(proxy_settings, is_unspecified));

  if (proto.proxy_option() ==
      sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
          PROXY_OPTION_AUTOMATIC) {
    if (proxy_settings->pac) {
      proto.set_autoconfiguration_url(proxy_settings->pac->active_value);
    }
  } else if (proto.proxy_option() ==
             sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
                 PROXY_OPTION_MANUAL) {
    sync_pb::
        WifiConfigurationSpecifics_ProxyConfiguration_ManualProxyConfiguration*
            manual_settings = proto.mutable_manual_proxy_configuration();

    if (proxy_settings->manual->http_proxy) {
      manual_settings->set_http_proxy_url(
          proxy_settings->manual->http_proxy->host->active_value);
      manual_settings->set_http_proxy_port(
          proxy_settings->manual->http_proxy->port->active_value);
    }

    if (proxy_settings->manual->secure_http_proxy) {
      manual_settings->set_secure_http_proxy_url(
          proxy_settings->manual->secure_http_proxy->host->active_value);
      manual_settings->set_secure_http_proxy_port(
          proxy_settings->manual->secure_http_proxy->port->active_value);
    }

    if (proxy_settings->manual->socks) {
      manual_settings->set_socks_host_url(
          proxy_settings->manual->socks->host->active_value);
      manual_settings->set_socks_host_port(
          proxy_settings->manual->socks->port->active_value);
    }

    if (proxy_settings->exclude_domains) {
      for (const std::string& domain :
           proxy_settings->exclude_domains->active_value) {
        manual_settings->add_excluded_domains(domain);
      }
    }
  }

  return proto;
}

network_config::mojom::SecurityType MojoSecurityTypeFromProto(
    const sync_pb::WifiConfigurationSpecifics_SecurityType& security_type) {
  switch (security_type) {
    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_PSK:
      return network_config::mojom::SecurityType::kWpaPsk;
    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_WEP:
      return network_config::mojom::SecurityType::kWepPsk;
    default:
      // Only PSK and WEP secured networks are supported by sync.
      NOTREACHED_IN_MIGRATION();
      return network_config::mojom::SecurityType::kNone;
  }
}

network_config::mojom::ProxySettingsPtr MojoProxySettingsFromProto(
    const sync_pb::WifiConfigurationSpecifics_ProxyConfiguration& proxy_proto) {
  auto proxy_settings = network_config::mojom::ProxySettings::New();
  switch (proxy_proto.proxy_option()) {
    case sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_AUTOMATIC:
      proxy_settings->type = ::onc::proxy::kPAC;
      proxy_settings->pac = proxy_proto.autoconfiguration_url();
      break;
    case sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_AUTODISCOVERY:
      proxy_settings->type = ::onc::proxy::kWPAD;
      break;
    case sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_MANUAL: {
      auto manual_settings = network_config::mojom::ManualProxySettings::New();
      auto synced_manual_configuration =
          proxy_proto.manual_proxy_configuration();
      proxy_settings->type = ::onc::proxy::kManual;

      manual_settings->http_proxy = network_config::mojom::ProxyLocation::New();
      manual_settings->http_proxy->host =
          synced_manual_configuration.http_proxy_url();
      manual_settings->http_proxy->port =
          synced_manual_configuration.http_proxy_port();

      manual_settings->secure_http_proxy =
          network_config::mojom::ProxyLocation::New();
      manual_settings->secure_http_proxy->host =
          synced_manual_configuration.secure_http_proxy_url();
      manual_settings->secure_http_proxy->port =
          synced_manual_configuration.secure_http_proxy_port();

      manual_settings->socks = network_config::mojom::ProxyLocation::New();
      manual_settings->socks->host =
          synced_manual_configuration.socks_host_url();
      manual_settings->socks->port =
          synced_manual_configuration.socks_host_port();

      proxy_settings->manual = std::move(manual_settings);

      std::vector<std::string> exclude_domains;
      for (const std::string& domain :
           synced_manual_configuration.excluded_domains()) {
        exclude_domains.push_back(domain);
      }
      proxy_settings->exclude_domains = std::move(exclude_domains);
      break;
    }
    case sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_DISABLED:
      proxy_settings->type = ::onc::proxy::kDirect;
      break;
    case sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
        PROXY_OPTION_UNSPECIFIED:
      break;
  }

  return proxy_settings;
}

network_config::mojom::ConfigPropertiesPtr MojoNetworkConfigFromProto(
    const sync_pb::WifiConfigurationSpecifics& specifics) {
  auto config = network_config::mojom::ConfigProperties::New();
  auto wifi = network_config::mojom::WiFiConfigProperties::New();

  wifi->ssid = DecodeHexString(specifics.hex_ssid());
  if (wifi->ssid->empty()) {
    // Return early instead of populating the other fields for a WifiConfig
    // because the SSID is the primary key for the network, so without this,
    // the rest of the properties are irrelevant.
    return nullptr;
  }

  wifi->security = MojoSecurityTypeFromProto(specifics.security_type());
  wifi->passphrase = specifics.passphrase();
  wifi->hidden_ssid = network_config::mojom::HiddenSsidMode::kDisabled;

  config->type_config =
      network_config::mojom::NetworkTypeConfigProperties::NewWifi(
          std::move(wifi));

  config->auto_connect = network_config::mojom::AutoConnectConfig::New(
      IsAutoconnectEnabled(specifics.automatically_connect()));

  if (specifics.has_is_preferred() &&
      specifics.is_preferred() !=
          sync_pb::WifiConfigurationSpecifics::IS_PREFERRED_UNSPECIFIED) {
    config->priority = network_config::mojom::PriorityConfig::New(
        specifics.is_preferred() ==
                sync_pb::WifiConfigurationSpecifics::IS_PREFERRED_ENABLED
            ? 1
            : 0);
  }

  // TODO(crbug/1128692): Restore support for the metered property when mojo
  // networks track the "Automatic" state.

  // For backwards compatibility, any available custom nameservers are still
  // applied when the dns_option is not set.
  if (specifics.dns_option() ==
          sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_CUSTOM ||
      (specifics.dns_option() ==
           sync_pb::
               WifiConfigurationSpecifics_DnsOption_DNS_OPTION_UNSPECIFIED &&
       specifics.custom_dns().size())) {
    auto ip_config = network_config::mojom::IPConfigProperties::New();
    std::vector<std::string> custom_dns;
    for (const std::string& nameserver : specifics.custom_dns()) {
      custom_dns.push_back(nameserver);
    }
    ip_config->name_servers = std::move(custom_dns);
    config->static_ip_config = std::move(ip_config);
    config->name_servers_config_type = onc::network_config::kIPConfigTypeStatic;
  } else if (specifics.dns_option() ==
             sync_pb::
                 WifiConfigurationSpecifics_DnsOption_DNS_OPTION_DEFAULT_DHCP) {
    config->name_servers_config_type = onc::network_config::kIPConfigTypeDHCP;
  }

  if (specifics.has_proxy_configuration() &&
      specifics.proxy_configuration().proxy_option() !=
          sync_pb::WifiConfigurationSpecifics_ProxyConfiguration::
              PROXY_OPTION_UNSPECIFIED) {
    config->proxy_settings =
        MojoProxySettingsFromProto(specifics.proxy_configuration());
  }

  return config;
}

const NetworkState* NetworkStateFromNetworkIdentifier(
    const NetworkIdentifier& id) {
  NetworkStateHandler::NetworkStateList networks;
  NetworkHandler::Get()->network_state_handler()->GetNetworkListByType(
      NetworkTypePattern::WiFi(), /*configured_only=*/true,
      /*visibleOnly=*/false, /*limit=*/0, &networks);
  for (const NetworkState* network : networks) {
    if (NetworkIdentifier::FromNetworkState(network) == id) {
      return network;
    }
  }
  return nullptr;
}

}  // namespace ash::sync_wifi