// 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/strings/stringprintf.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_profile_handler.h" #include "chromeos/network/network_type_pattern.h" #include "chromeos/network/network_util.h" #include "chromeos/network/onc/onc_utils.h" #include "chromeos/network/shill_property_util.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; } bool IsCaCertNssSet(const base::DictionaryValue& properties) { std::string ca_cert_nss; if (properties.GetStringWithoutPathExpansion(shill::kEapCaCertNssProperty, &ca_cert_nss) && !ca_cert_nss.empty()) { return true; } const base::DictionaryValue* provider = NULL; properties.GetDictionaryWithoutPathExpansion(shill::kProviderProperty, &provider); if (!provider) return false; if (provider->GetStringWithoutPathExpansion( shill::kL2tpIpsecCaCertNssProperty, &ca_cert_nss) && !ca_cert_nss.empty()) { return true; } if (provider->GetStringWithoutPathExpansion( shill::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), visible_(false), connectable_(false), prefix_length_(0), signal_strength_(0), 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 == shill::kSignalStrengthProperty) { return GetIntegerValue(key, value, &signal_strength_); } else if (key == shill::kStateProperty) { return GetStringValue(key, value, &connection_state_); } else if (key == shill::kVisibleProperty) { return GetBooleanValue(key, value, &visible_); } else if (key == shill::kConnectableProperty) { return GetBooleanValue(key, value, &connectable_); } else if (key == shill::kErrorProperty) { if (!GetStringValue(key, value, &error_)) return false; if (ErrorIsValid(error_)) last_error_ = error_; else error_.clear(); return true; } else if (key == shill::kActivationStateProperty) { return GetStringValue(key, value, &activation_state_); } else if (key == shill::kRoamingStateProperty) { return GetStringValue(key, value, &roaming_); } else if (key == shill::kSecurityProperty) { return GetStringValue(key, value, &security_); } else if (key == shill::kEapMethodProperty) { return GetStringValue(key, value, &eap_method_); } else if (key == shill::kUIDataProperty) { scoped_ptr<NetworkUIData> new_ui_data = shill_property_util::GetUIDataFromValue(value); if (!new_ui_data) { NET_LOG_ERROR("Failed to parse " + key, path()); return false; } ui_data_ = *new_ui_data; return true; } else if (key == shill::kNetworkTechnologyProperty) { return GetStringValue(key, value, &network_technology_); } else if (key == shill::kDeviceProperty) { return GetStringValue(key, value, &device_path_); } else if (key == shill::kGuidProperty) { return GetStringValue(key, value, &guid_); } else if (key == shill::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 == shill::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; } return false; } bool NetworkState::InitialPropertiesReceived( const base::DictionaryValue& properties) { NET_LOG_DEBUG("InitialPropertiesReceived", path()); bool changed = false; if (!properties.HasKey(shill::kTypeProperty)) { NET_LOG_ERROR("NetworkState has no type", shill_property_util::GetNetworkIdFromProperties(properties)); return false; } // Ensure that the network has a valid name. changed |= UpdateName(properties); // Set the has_ca_cert_nss_ property. bool had_ca_cert_nss = has_ca_cert_nss_; has_ca_cert_nss_ = IsCaCertNssSet(properties); changed |= had_ca_cert_nss != has_ca_cert_nss_; // By convention, all visible WiFi networks have a SignalStrength > 0. if (visible() && type() == shill::kTypeWifi) { if (signal_strength_ <= 0) signal_strength_ = 1; } return changed; } void NetworkState::GetStateProperties(base::DictionaryValue* dictionary) const { ManagedState::GetStateProperties(dictionary); // Properties shared by all types. dictionary->SetStringWithoutPathExpansion(shill::kGuidProperty, guid()); dictionary->SetStringWithoutPathExpansion(shill::kSecurityProperty, security()); if (visible()) { if (!error().empty()) dictionary->SetStringWithoutPathExpansion(shill::kErrorProperty, error()); dictionary->SetStringWithoutPathExpansion(shill::kStateProperty, connection_state()); } // Wireless properties if (!NetworkTypePattern::Wireless().MatchesType(type())) return; if (visible()) { dictionary->SetBooleanWithoutPathExpansion(shill::kConnectableProperty, connectable()); dictionary->SetIntegerWithoutPathExpansion(shill::kSignalStrengthProperty, signal_strength()); } // Wifi properties if (NetworkTypePattern::WiFi().MatchesType(type())) { dictionary->SetStringWithoutPathExpansion(shill::kEapMethodProperty, eap_method()); } // Mobile properties if (NetworkTypePattern::Mobile().MatchesType(type())) { dictionary->SetStringWithoutPathExpansion( shill::kNetworkTechnologyProperty, network_technology()); dictionary->SetStringWithoutPathExpansion(shill::kActivationStateProperty, activation_state()); dictionary->SetStringWithoutPathExpansion(shill::kRoamingStateProperty, roaming()); dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty, cellular_out_of_credits()); } } void NetworkState::IPConfigPropertiesChanged( const base::DictionaryValue& properties) { for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd(); iter.Advance()) { std::string key = iter.key(); const base::Value& value = iter.value(); if (key == shill::kAddressProperty) { GetStringValue(key, value, &ip_address_); } else if (key == shill::kGatewayProperty) { GetStringValue(key, value, &gateway_); } else if (key == shill::kNameServersProperty) { const base::ListValue* dns_servers; if (value.GetAsList(&dns_servers)) { dns_servers_.clear(); ConvertListValueToStringVector(*dns_servers, &dns_servers_); } } else if (key == shill::kPrefixlenProperty) { GetIntegerValue(key, value, &prefix_length_); } else if (key == shill::kWebProxyAutoDiscoveryUrlProperty) { std::string url_string; if (GetStringValue(key, value, &url_string)) { if (url_string.empty()) { web_proxy_auto_discovery_url_ = GURL(); } else { GURL gurl(url_string); if (gurl.is_valid()) { web_proxy_auto_discovery_url_ = gurl; } else { NET_LOG_ERROR("Invalid WebProxyAutoDiscoveryUrl: " + url_string, path()); web_proxy_auto_discovery_url_ = GURL(); } } } } } } bool NetworkState::RequiresActivation() const { return (type() == shill::kTypeCellular && activation_state() != shill::kActivationStateActivated && activation_state() != shill::kActivationStateUnknown); } std::string NetworkState::connection_state() const { if (!visible()) return shill::kStateDisconnect; return connection_state_; } bool NetworkState::IsConnectedState() const { return visible() && StateIsConnected(connection_state_); } bool NetworkState::IsConnectingState() const { return visible() && StateIsConnecting(connection_state_); } bool NetworkState::IsInProfile() const { // kTypeEthernetEap is always saved. We need this check because it does // not show up in the visible list, but its properties may not be available // when it first shows up in ServiceCompleteList. See crbug.com/355117. return !profile_path_.empty() || type() == shill::kTypeEthernetEap; } bool NetworkState::IsPrivate() const { return !profile_path_.empty() && profile_path_ != NetworkProfileHandler::GetSharedProfilePath(); } 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_); } std::string NetworkState::GetSpecifier() const { if (!update_received()) { NET_LOG_ERROR("GetSpecifier called before update", path()); return std::string(); } if (type() == shill::kTypeWifi) return name() + "_" + security_; if (!name().empty()) return name(); return type(); // For unnamed networks such as ethernet. } void NetworkState::SetGuid(const std::string& guid) { guid_ = guid; } bool NetworkState::UpdateName(const base::DictionaryValue& properties) { std::string updated_name = shill_property_util::GetNameFromProperties(path(), properties); if (updated_name != name()) { set_name(updated_name); return true; } return false; } // static bool NetworkState::StateIsConnected(const std::string& connection_state) { return (connection_state == shill::kStateReady || connection_state == shill::kStateOnline || connection_state == shill::kStatePortal); } // static bool NetworkState::StateIsConnecting(const std::string& connection_state) { return (connection_state == shill::kStateAssociation || connection_state == shill::kStateConfiguration || connection_state == shill::kStateCarrier); } // static bool NetworkState::ErrorIsValid(const std::string& error) { // Shill uses "Unknown" to indicate an unset or cleared error state. return !error.empty() && error != kErrorUnknown; } } // namespace chromeos