blob: deff6475dec31fab136229afff8bd34ece3353a0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/network/network_state.h"
#include "base/i18n/icu_encoding_detection.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_utils.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace {
const char kErrorUnknown[] = "Unknown";
bool ConvertListValueToStringVector(const base::ListValue& string_list,
std::vector<std::string>* result) {
for (size_t i = 0; i < string_list.GetSize(); ++i) {
std::string str;
if (!string_list.GetString(i, &str))
return false;
result->push_back(str);
}
return true;
}
// Replace non UTF8 characters in |str| with a replacement character.
std::string ValidateUTF8(const std::string& str) {
std::string result;
for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) {
uint32 code_point_out;
bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(),
&index, &code_point_out);
const uint32 kFirstNonControlChar = 0x20;
if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) {
base::WriteUnicodeCharacter(code_point_out, &result);
} else {
const uint32 kReplacementChar = 0xFFFD;
// Puts kReplacementChar if character is a control character [0,0x20)
// or is not readable UTF8.
base::WriteUnicodeCharacter(kReplacementChar, &result);
}
}
return result;
}
bool IsCaCertNssSet(const base::DictionaryValue& properties) {
std::string ca_cert_nss;
if (properties.GetStringWithoutPathExpansion(flimflam::kEapCaCertNssProperty,
&ca_cert_nss) &&
!ca_cert_nss.empty()) {
return true;
}
const base::DictionaryValue* provider = NULL;
properties.GetDictionaryWithoutPathExpansion(flimflam::kProviderProperty,
&provider);
if (!provider)
return false;
if (provider->GetStringWithoutPathExpansion(
flimflam::kL2tpIpsecCaCertNssProperty, &ca_cert_nss) &&
!ca_cert_nss.empty()) {
return true;
}
if (provider->GetStringWithoutPathExpansion(
flimflam::kOpenVPNCaCertNSSProperty, &ca_cert_nss) &&
!ca_cert_nss.empty()) {
return true;
}
return false;
}
} // namespace
namespace chromeos {
NetworkState::NetworkState(const std::string& path)
: ManagedState(MANAGED_TYPE_NETWORK, path),
auto_connect_(false),
favorite_(false),
priority_(0),
prefix_length_(0),
signal_strength_(0),
connectable_(false),
activate_over_non_cellular_networks_(false),
cellular_out_of_credits_(false),
has_ca_cert_nss_(false) {
}
NetworkState::~NetworkState() {
}
bool NetworkState::PropertyChanged(const std::string& key,
const base::Value& value) {
// Keep care that these properties are the same as in |GetProperties|.
if (ManagedStatePropertyChanged(key, value))
return true;
if (key == flimflam::kSignalStrengthProperty) {
return GetIntegerValue(key, value, &signal_strength_);
} else if (key == flimflam::kStateProperty) {
return GetStringValue(key, value, &connection_state_);
} else if (key == flimflam::kConnectableProperty) {
return GetBooleanValue(key, value, &connectable_);
} else if (key == flimflam::kErrorProperty) {
if (!GetStringValue(key, value, &error_))
return false;
// Shill uses "Unknown" to indicate an unset error state.
if (error_ == kErrorUnknown)
error_.clear();
return true;
} else if (key == shill::kErrorDetailsProperty) {
return GetStringValue(key, value, &error_details_);
} else if (key == IPConfigProperty(flimflam::kAddressProperty)) {
return GetStringValue(key, value, &ip_address_);
} else if (key == IPConfigProperty(flimflam::kGatewayProperty)) {
return GetStringValue(key, value, &gateway_);
} else if (key == IPConfigProperty(flimflam::kNameServersProperty)) {
const base::ListValue* dns_servers;
if (!value.GetAsList(&dns_servers))
return false;
dns_servers_.clear();
ConvertListValueToStringVector(*dns_servers, &dns_servers_);
return true;
} else if (key == IPConfigProperty(flimflam::kPrefixlenProperty)) {
return GetIntegerValue(key, value, &prefix_length_);
} else if (key == flimflam::kActivationStateProperty) {
return GetStringValue(key, value, &activation_state_);
} else if (key == flimflam::kRoamingStateProperty) {
return GetStringValue(key, value, &roaming_);
} else if (key == flimflam::kSecurityProperty) {
return GetStringValue(key, value, &security_);
} else if (key == flimflam::kAutoConnectProperty) {
return GetBooleanValue(key, value, &auto_connect_);
} else if (key == flimflam::kFavoriteProperty) {
return GetBooleanValue(key, value, &favorite_);
} else if (key == flimflam::kPriorityProperty) {
return GetIntegerValue(key, value, &priority_);
} else if (key == flimflam::kProxyConfigProperty) {
std::string proxy_config_str;
if (!value.GetAsString(&proxy_config_str)) {
NET_LOG_ERROR("Failed to parse " + key, path());
return false;
}
proxy_config_.Clear();
if (proxy_config_str.empty())
return true;
scoped_ptr<base::DictionaryValue> proxy_config_dict(
onc::ReadDictionaryFromJson(proxy_config_str));
if (proxy_config_dict) {
// Warning: The DictionaryValue returned from
// ReadDictionaryFromJson/JSONParser is an optimized derived class that
// doesn't allow releasing ownership of nested values. A Swap in the wrong
// order leads to memory access errors.
proxy_config_.MergeDictionary(proxy_config_dict.get());
} else {
NET_LOG_ERROR("Failed to parse " + key, path());
}
return true;
} else if (key == flimflam::kUIDataProperty) {
if (!GetUIDataFromValue(value, &ui_data_)) {
NET_LOG_ERROR("Failed to parse " + key, path());
return false;
}
return true;
} else if (key == flimflam::kNetworkTechnologyProperty) {
return GetStringValue(key, value, &network_technology_);
} else if (key == flimflam::kDeviceProperty) {
return GetStringValue(key, value, &device_path_);
} else if (key == flimflam::kGuidProperty) {
return GetStringValue(key, value, &guid_);
} else if (key == flimflam::kProfileProperty) {
return GetStringValue(key, value, &profile_path_);
} else if (key == shill::kActivateOverNonCellularNetworkProperty) {
return GetBooleanValue(key, value, &activate_over_non_cellular_networks_);
} else if (key == shill::kOutOfCreditsProperty) {
return GetBooleanValue(key, value, &cellular_out_of_credits_);
} else if (key == flimflam::kUsageURLProperty) {
return GetStringValue(key, value, &usage_url_);
} else if (key == flimflam::kPaymentPortalProperty) {
const DictionaryValue* dict;
if (!value.GetAsDictionary(&dict))
return false;
if (!dict->GetStringWithoutPathExpansion(
flimflam::kPaymentPortalURL, &payment_url_) ||
!dict->GetStringWithoutPathExpansion(
flimflam::kPaymentPortalMethod, &post_method_) ||
!dict->GetStringWithoutPathExpansion(
flimflam::kPaymentPortalPostData, &post_data_)) {
return false;
}
return true;
}
return false;
}
bool NetworkState::InitialPropertiesReceived(
const base::DictionaryValue& properties) {
bool changed = UpdateName(properties);
bool had_ca_cert_nss = has_ca_cert_nss_;
has_ca_cert_nss_ = IsCaCertNssSet(properties);
changed |= had_ca_cert_nss != has_ca_cert_nss_;
return changed;
}
void NetworkState::GetProperties(base::DictionaryValue* dictionary) const {
// Keep care that these properties are the same as in |PropertyChanged|.
dictionary->SetStringWithoutPathExpansion(flimflam::kNameProperty, name());
dictionary->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type());
dictionary->SetIntegerWithoutPathExpansion(flimflam::kSignalStrengthProperty,
signal_strength_);
dictionary->SetStringWithoutPathExpansion(flimflam::kStateProperty,
connection_state_);
dictionary->SetBooleanWithoutPathExpansion(flimflam::kConnectableProperty,
connectable_);
dictionary->SetStringWithoutPathExpansion(flimflam::kErrorProperty,
error_);
dictionary->SetStringWithoutPathExpansion(shill::kErrorDetailsProperty,
error_details_);
// IPConfig properties
base::DictionaryValue* ipconfig_properties = new base::DictionaryValue;
ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kAddressProperty,
ip_address_);
ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kGatewayProperty,
gateway_);
base::ListValue* name_servers = new base::ListValue;
name_servers->AppendStrings(dns_servers_);
ipconfig_properties->SetWithoutPathExpansion(flimflam::kNameServersProperty,
name_servers);
ipconfig_properties->SetIntegerWithoutPathExpansion(
flimflam::kPrefixlenProperty, prefix_length_);
dictionary->SetWithoutPathExpansion(shill::kIPConfigProperty,
ipconfig_properties);
dictionary->SetStringWithoutPathExpansion(flimflam::kActivationStateProperty,
activation_state_);
dictionary->SetStringWithoutPathExpansion(flimflam::kRoamingStateProperty,
roaming_);
dictionary->SetStringWithoutPathExpansion(flimflam::kSecurityProperty,
security_);
dictionary->SetBooleanWithoutPathExpansion(flimflam::kAutoConnectProperty,
auto_connect_);
dictionary->SetBooleanWithoutPathExpansion(flimflam::kFavoriteProperty,
favorite_);
dictionary->SetIntegerWithoutPathExpansion(flimflam::kPriorityProperty,
priority_);
// Proxy config and ONC source are intentionally omitted: These properties are
// placed in NetworkState to transition ProxyConfigServiceImpl from
// NetworkLibrary to the new network stack. The networking extension API
// shouldn't depend on this member. Once ManagedNetworkConfigurationHandler
// is used instead of NetworkLibrary, we can remove them again.
dictionary->SetStringWithoutPathExpansion(
flimflam::kNetworkTechnologyProperty,
network_technology_);
dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty,
device_path_);
dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid_);
dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty,
profile_path_);
dictionary->SetBooleanWithoutPathExpansion(
shill::kActivateOverNonCellularNetworkProperty,
activate_over_non_cellular_networks_);
dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty,
cellular_out_of_credits_);
base::DictionaryValue* payment_portal_properties = new DictionaryValue;
payment_portal_properties->SetStringWithoutPathExpansion(
flimflam::kPaymentPortalURL,
payment_url_);
payment_portal_properties->SetStringWithoutPathExpansion(
flimflam::kPaymentPortalMethod,
post_method_);
payment_portal_properties->SetStringWithoutPathExpansion(
flimflam::kPaymentPortalPostData,
post_data_);
dictionary->SetWithoutPathExpansion(flimflam::kPaymentPortalProperty,
payment_portal_properties);
}
bool NetworkState::IsConnectedState() const {
return StateIsConnected(connection_state_);
}
bool NetworkState::IsConnectingState() const {
return StateIsConnecting(connection_state_);
}
bool NetworkState::IsManaged() const {
return ui_data_.onc_source() == onc::ONC_SOURCE_DEVICE_POLICY ||
ui_data_.onc_source() == onc::ONC_SOURCE_USER_POLICY;
}
bool NetworkState::IsPrivate() const {
return !profile_path_.empty() &&
profile_path_ != NetworkProfileHandler::kSharedProfilePath;
}
std::string NetworkState::GetDnsServersAsString() const {
std::string result;
for (size_t i = 0; i < dns_servers_.size(); ++i) {
if (i != 0)
result += ",";
result += dns_servers_[i];
}
return result;
}
std::string NetworkState::GetNetmask() const {
return network_util::PrefixLengthToNetmask(prefix_length_);
}
bool NetworkState::UpdateName(const base::DictionaryValue& properties) {
std::string updated_name = GetNameFromProperties(properties);
if (updated_name != name()) {
set_name(updated_name);
return true;
}
return false;
}
// static
std::string NetworkState::GetNameFromProperties(
const base::DictionaryValue& properties) {
std::string name;
properties.GetStringWithoutPathExpansion(flimflam::kNameProperty, &name);
std::string hex_ssid;
properties.GetStringWithoutPathExpansion(flimflam::kWifiHexSsid, &hex_ssid);
if (hex_ssid.empty()) {
// Validate name for UTF8.
std::string valid_ssid = ValidateUTF8(name);
if (valid_ssid != name) {
NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
"%s: UTF8: %s", name.c_str(), valid_ssid.c_str()));
}
return valid_ssid;
}
std::string ssid;
std::vector<uint8> raw_ssid_bytes;
if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
} else {
NET_LOG_ERROR("GetNameFromProperties",
base::StringPrintf("%s: Error processing: %s",
name.c_str(), hex_ssid.c_str()));
return name;
}
if (IsStringUTF8(ssid)) {
if (ssid != name) {
NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
"%s: UTF8: %s", name.c_str(), ssid.c_str()));
}
return ssid;
}
// Detect encoding and convert to UTF-8.
std::string country_code;
properties.GetStringWithoutPathExpansion(
flimflam::kCountryProperty, &country_code);
std::string encoding;
if (!base::DetectEncoding(ssid, &encoding)) {
// TODO(stevenjb): This is currently experimental. If we find a case where
// base::DetectEncoding() fails, we need to figure out whether we can use
// country_code with ConvertToUtf8(). crbug.com/233267.
encoding = country_code;
}
if (!encoding.empty()) {
std::string utf8_ssid;
if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
if (utf8_ssid != name) {
NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
"%s: Encoding=%s: %s", name.c_str(),
encoding.c_str(), utf8_ssid.c_str()));
}
return utf8_ssid;
}
}
// Unrecognized encoding. Only use raw bytes if name_ is empty.
NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
"%s: Unrecognized Encoding=%s: %s", name.c_str(),
encoding.c_str(), ssid.c_str()));
if (name.empty() && !ssid.empty())
return ssid;
return name;
}
// static
bool NetworkState::StateIsConnected(const std::string& connection_state) {
return (connection_state == flimflam::kStateReady ||
connection_state == flimflam::kStateOnline ||
connection_state == flimflam::kStatePortal);
}
// static
bool NetworkState::StateIsConnecting(const std::string& connection_state) {
return (connection_state == flimflam::kStateAssociation ||
connection_state == flimflam::kStateConfiguration ||
connection_state == flimflam::kStateCarrier);
}
// static
std::string NetworkState::IPConfigProperty(const char* key) {
return base::StringPrintf("%s.%s", shill::kIPConfigProperty, key);
}
// static
bool NetworkState::GetUIDataFromValue(const base::Value& ui_data_value,
NetworkUIData* out) {
std::string ui_data_str;
if (!ui_data_value.GetAsString(&ui_data_str))
return false;
if (ui_data_str.empty()) {
*out = NetworkUIData();
return true;
}
scoped_ptr<base::DictionaryValue> ui_data_dict(
chromeos::onc::ReadDictionaryFromJson(ui_data_str));
if (!ui_data_dict)
return false;
*out = NetworkUIData(*ui_data_dict);
return true;
}
} // namespace chromeos