network_state.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chromeos/network/network_state.h" 6 7#include "base/i18n/icu_encoding_detection.h" 8#include "base/i18n/icu_string_conversions.h" 9#include "base/json/json_writer.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/string_util.h" 12#include "base/strings/stringprintf.h" 13#include "base/strings/utf_string_conversion_utils.h" 14#include "chromeos/network/network_event_log.h" 15#include "chromeos/network/network_profile_handler.h" 16#include "chromeos/network/network_ui_data.h" 17#include "chromeos/network/network_util.h" 18#include "chromeos/network/onc/onc_utils.h" 19#include "third_party/cros_system_api/dbus/service_constants.h" 20 21namespace { 22 23bool ConvertListValueToStringVector(const base::ListValue& string_list, 24 std::vector<std::string>* result) { 25 for (size_t i = 0; i < string_list.GetSize(); ++i) { 26 std::string str; 27 if (!string_list.GetString(i, &str)) 28 return false; 29 result->push_back(str); 30 } 31 return true; 32} 33 34// Replace non UTF8 characters in |str| with a replacement character. 35std::string ValidateUTF8(const std::string& str) { 36 std::string result; 37 for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) { 38 uint32 code_point_out; 39 bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(), 40 &index, &code_point_out); 41 const uint32 kFirstNonControlChar = 0x20; 42 if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) { 43 base::WriteUnicodeCharacter(code_point_out, &result); 44 } else { 45 const uint32 kReplacementChar = 0xFFFD; 46 // Puts kReplacementChar if character is a control character [0,0x20) 47 // or is not readable UTF8. 48 base::WriteUnicodeCharacter(kReplacementChar, &result); 49 } 50 } 51 return result; 52} 53 54// Returns a new NetworkUIData* if |ui_data_value| is a valid NetworkUIData 55// dictionary string, otherwise returns NULL. 56chromeos::NetworkUIData* CreateUIDataFromValue( 57 const base::Value& ui_data_value) { 58 std::string ui_data_str; 59 if (!ui_data_value.GetAsString(&ui_data_str)) 60 return NULL; 61 if (ui_data_str.empty()) 62 return new chromeos::NetworkUIData(); 63 64 scoped_ptr<base::DictionaryValue> ui_data_dict( 65 chromeos::onc::ReadDictionaryFromJson(ui_data_str)); 66 if (!ui_data_dict) 67 return NULL; 68 return new chromeos::NetworkUIData(*ui_data_dict); 69} 70 71bool IsCaCertNssSet(const base::DictionaryValue& properties) { 72 std::string ca_cert_nss; 73 if (properties.GetStringWithoutPathExpansion(flimflam::kEapCaCertNssProperty, 74 &ca_cert_nss) && 75 !ca_cert_nss.empty()) { 76 return true; 77 } 78 79 const base::DictionaryValue* provider = NULL; 80 properties.GetDictionaryWithoutPathExpansion(flimflam::kProviderProperty, 81 &provider); 82 if (!provider) 83 return false; 84 if (provider->GetStringWithoutPathExpansion( 85 flimflam::kL2tpIpsecCaCertNssProperty, &ca_cert_nss) && 86 !ca_cert_nss.empty()) { 87 return true; 88 } 89 if (provider->GetStringWithoutPathExpansion( 90 flimflam::kOpenVPNCaCertNSSProperty, &ca_cert_nss) && 91 !ca_cert_nss.empty()) { 92 return true; 93 } 94 95 return false; 96} 97 98} // namespace 99 100namespace chromeos { 101 102NetworkState::NetworkState(const std::string& path) 103 : ManagedState(MANAGED_TYPE_NETWORK, path), 104 auto_connect_(false), 105 favorite_(false), 106 priority_(0), 107 onc_source_(onc::ONC_SOURCE_NONE), 108 prefix_length_(0), 109 signal_strength_(0), 110 connectable_(false), 111 activate_over_non_cellular_networks_(false), 112 cellular_out_of_credits_(false), 113 has_ca_cert_nss_(false) { 114} 115 116NetworkState::~NetworkState() { 117} 118 119bool NetworkState::PropertyChanged(const std::string& key, 120 const base::Value& value) { 121 // Keep care that these properties are the same as in |GetProperties|. 122 if (ManagedStatePropertyChanged(key, value)) 123 return true; 124 if (key == flimflam::kSignalStrengthProperty) { 125 return GetIntegerValue(key, value, &signal_strength_); 126 } else if (key == flimflam::kStateProperty) { 127 return GetStringValue(key, value, &connection_state_); 128 } else if (key == flimflam::kConnectableProperty) { 129 return GetBooleanValue(key, value, &connectable_); 130 } else if (key == flimflam::kErrorProperty) { 131 return GetStringValue(key, value, &error_); 132 } else if (key == shill::kErrorDetailsProperty) { 133 return GetStringValue(key, value, &error_details_); 134 } else if (key == IPConfigProperty(flimflam::kAddressProperty)) { 135 return GetStringValue(key, value, &ip_address_); 136 } else if (key == IPConfigProperty(flimflam::kGatewayProperty)) { 137 return GetStringValue(key, value, &gateway_); 138 } else if (key == IPConfigProperty(flimflam::kNameServersProperty)) { 139 const base::ListValue* dns_servers; 140 if (!value.GetAsList(&dns_servers)) 141 return false; 142 dns_servers_.clear(); 143 ConvertListValueToStringVector(*dns_servers, &dns_servers_); 144 return true; 145 } else if (key == IPConfigProperty(flimflam::kPrefixlenProperty)) { 146 return GetIntegerValue(key, value, &prefix_length_); 147 } else if (key == flimflam::kActivationStateProperty) { 148 return GetStringValue(key, value, &activation_state_); 149 } else if (key == flimflam::kRoamingStateProperty) { 150 return GetStringValue(key, value, &roaming_); 151 } else if (key == flimflam::kSecurityProperty) { 152 return GetStringValue(key, value, &security_); 153 } else if (key == flimflam::kAutoConnectProperty) { 154 return GetBooleanValue(key, value, &auto_connect_); 155 } else if (key == flimflam::kFavoriteProperty) { 156 return GetBooleanValue(key, value, &favorite_); 157 } else if (key == flimflam::kPriorityProperty) { 158 return GetIntegerValue(key, value, &priority_); 159 } else if (key == flimflam::kProxyConfigProperty) { 160 std::string proxy_config_str; 161 if (!value.GetAsString(&proxy_config_str)) { 162 NET_LOG_ERROR("Failed to parse " + key, path()); 163 return false; 164 } 165 166 proxy_config_.Clear(); 167 if (proxy_config_str.empty()) 168 return true; 169 170 scoped_ptr<base::DictionaryValue> proxy_config_dict( 171 onc::ReadDictionaryFromJson(proxy_config_str)); 172 if (proxy_config_dict) { 173 // Warning: The DictionaryValue returned from 174 // ReadDictionaryFromJson/JSONParser is an optimized derived class that 175 // doesn't allow releasing ownership of nested values. A Swap in the wrong 176 // order leads to memory access errors. 177 proxy_config_.MergeDictionary(proxy_config_dict.get()); 178 } else { 179 NET_LOG_ERROR("Failed to parse " + key, path()); 180 } 181 return true; 182 } else if (key == flimflam::kUIDataProperty) { 183 if (!GetOncSource(value, &onc_source_)) { 184 NET_LOG_ERROR("Failed to parse " + key, path()); 185 return false; 186 } 187 return true; 188 } else if (key == flimflam::kNetworkTechnologyProperty) { 189 return GetStringValue(key, value, &network_technology_); 190 } else if (key == flimflam::kDeviceProperty) { 191 return GetStringValue(key, value, &device_path_); 192 } else if (key == flimflam::kGuidProperty) { 193 return GetStringValue(key, value, &guid_); 194 } else if (key == flimflam::kProfileProperty) { 195 return GetStringValue(key, value, &profile_path_); 196 } else if (key == shill::kActivateOverNonCellularNetworkProperty) { 197 return GetBooleanValue(key, value, &activate_over_non_cellular_networks_); 198 } else if (key == shill::kOutOfCreditsProperty) { 199 return GetBooleanValue(key, value, &cellular_out_of_credits_); 200 } else if (key == flimflam::kUsageURLProperty) { 201 return GetStringValue(key, value, &usage_url_); 202 } else if (key == flimflam::kPaymentPortalProperty) { 203 const DictionaryValue* dict; 204 if (!value.GetAsDictionary(&dict)) 205 return false; 206 if (!dict->GetStringWithoutPathExpansion( 207 flimflam::kPaymentPortalURL, &payment_url_) || 208 !dict->GetStringWithoutPathExpansion( 209 flimflam::kPaymentPortalMethod, &post_method_) || 210 !dict->GetStringWithoutPathExpansion( 211 flimflam::kPaymentPortalPostData, &post_data_)) { 212 return false; 213 } 214 return true; 215 } 216 return false; 217} 218 219bool NetworkState::InitialPropertiesReceived( 220 const base::DictionaryValue& properties) { 221 bool changed = UpdateName(properties); 222 bool had_ca_cert_nss = has_ca_cert_nss_; 223 has_ca_cert_nss_ = IsCaCertNssSet(properties); 224 changed |= had_ca_cert_nss != has_ca_cert_nss_; 225 return changed; 226} 227 228void NetworkState::GetProperties(base::DictionaryValue* dictionary) const { 229 // Keep care that these properties are the same as in |PropertyChanged|. 230 dictionary->SetStringWithoutPathExpansion(flimflam::kNameProperty, name()); 231 dictionary->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type()); 232 dictionary->SetIntegerWithoutPathExpansion(flimflam::kSignalStrengthProperty, 233 signal_strength_); 234 dictionary->SetStringWithoutPathExpansion(flimflam::kStateProperty, 235 connection_state_); 236 dictionary->SetBooleanWithoutPathExpansion(flimflam::kConnectableProperty, 237 connectable_); 238 239 dictionary->SetStringWithoutPathExpansion(flimflam::kErrorProperty, 240 error_); 241 dictionary->SetStringWithoutPathExpansion(shill::kErrorDetailsProperty, 242 error_details_); 243 244 // IPConfig properties 245 base::DictionaryValue* ipconfig_properties = new base::DictionaryValue; 246 ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kAddressProperty, 247 ip_address_); 248 ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kGatewayProperty, 249 gateway_); 250 base::ListValue* name_servers = new base::ListValue; 251 name_servers->AppendStrings(dns_servers_); 252 ipconfig_properties->SetWithoutPathExpansion(flimflam::kNameServersProperty, 253 name_servers); 254 ipconfig_properties->SetIntegerWithoutPathExpansion( 255 flimflam::kPrefixlenProperty, prefix_length_); 256 257 dictionary->SetWithoutPathExpansion(shill::kIPConfigProperty, 258 ipconfig_properties); 259 260 dictionary->SetStringWithoutPathExpansion(flimflam::kActivationStateProperty, 261 activation_state_); 262 dictionary->SetStringWithoutPathExpansion(flimflam::kRoamingStateProperty, 263 roaming_); 264 dictionary->SetStringWithoutPathExpansion(flimflam::kSecurityProperty, 265 security_); 266 dictionary->SetBooleanWithoutPathExpansion(flimflam::kAutoConnectProperty, 267 auto_connect_); 268 dictionary->SetBooleanWithoutPathExpansion(flimflam::kFavoriteProperty, 269 favorite_); 270 dictionary->SetIntegerWithoutPathExpansion(flimflam::kPriorityProperty, 271 priority_); 272 // Proxy config and ONC source are intentionally omitted: These properties are 273 // placed in NetworkState to transition ProxyConfigServiceImpl from 274 // NetworkLibrary to the new network stack. The networking extension API 275 // shouldn't depend on this member. Once ManagedNetworkConfigurationHandler 276 // is used instead of NetworkLibrary, we can remove them again. 277 dictionary->SetStringWithoutPathExpansion( 278 flimflam::kNetworkTechnologyProperty, 279 network_technology_); 280 dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty, 281 device_path_); 282 dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid_); 283 dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, 284 profile_path_); 285 dictionary->SetBooleanWithoutPathExpansion( 286 shill::kActivateOverNonCellularNetworkProperty, 287 activate_over_non_cellular_networks_); 288 dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty, 289 cellular_out_of_credits_); 290 base::DictionaryValue* payment_portal_properties = new DictionaryValue; 291 payment_portal_properties->SetStringWithoutPathExpansion( 292 flimflam::kPaymentPortalURL, 293 payment_url_); 294 payment_portal_properties->SetStringWithoutPathExpansion( 295 flimflam::kPaymentPortalMethod, 296 post_method_); 297 payment_portal_properties->SetStringWithoutPathExpansion( 298 flimflam::kPaymentPortalPostData, 299 post_data_); 300 dictionary->SetWithoutPathExpansion(flimflam::kPaymentPortalProperty, 301 payment_portal_properties); 302} 303 304bool NetworkState::IsConnectedState() const { 305 return StateIsConnected(connection_state_); 306} 307 308bool NetworkState::IsConnectingState() const { 309 return StateIsConnecting(connection_state_); 310} 311 312bool NetworkState::IsManaged() const { 313 return onc_source_ == onc::ONC_SOURCE_DEVICE_POLICY || 314 onc_source_ == onc::ONC_SOURCE_USER_POLICY; 315} 316 317bool NetworkState::IsPrivate() const { 318 return !profile_path_.empty() && 319 profile_path_ != NetworkProfileHandler::kSharedProfilePath; 320} 321 322std::string NetworkState::GetDnsServersAsString() const { 323 std::string result; 324 for (size_t i = 0; i < dns_servers_.size(); ++i) { 325 if (i != 0) 326 result += ","; 327 result += dns_servers_[i]; 328 } 329 return result; 330} 331 332std::string NetworkState::GetNetmask() const { 333 return network_util::PrefixLengthToNetmask(prefix_length_); 334} 335 336bool NetworkState::UpdateName(const base::DictionaryValue& properties) { 337 std::string updated_name = GetNameFromProperties(properties); 338 if (updated_name != name()) { 339 set_name(updated_name); 340 return true; 341 } 342 return false; 343} 344 345// static 346std::string NetworkState::GetNameFromProperties( 347 const base::DictionaryValue& properties) { 348 std::string name; 349 properties.GetStringWithoutPathExpansion(flimflam::kNameProperty, &name); 350 351 std::string hex_ssid; 352 properties.GetStringWithoutPathExpansion(flimflam::kWifiHexSsid, &hex_ssid); 353 354 if (hex_ssid.empty()) { 355 // Validate name for UTF8. 356 std::string valid_ssid = ValidateUTF8(name); 357 if (valid_ssid != name) { 358 NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf( 359 "%s: UTF8: %s", name.c_str(), valid_ssid.c_str())); 360 } 361 return valid_ssid; 362 } 363 364 std::string ssid; 365 std::vector<uint8> raw_ssid_bytes; 366 if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) { 367 ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end()); 368 } else { 369 NET_LOG_ERROR("GetNameFromProperties", 370 base::StringPrintf("%s: Error processing: %s", 371 name.c_str(), hex_ssid.c_str())); 372 return name; 373 } 374 375 if (IsStringUTF8(ssid)) { 376 if (ssid != name) { 377 NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf( 378 "%s: UTF8: %s", name.c_str(), ssid.c_str())); 379 } 380 return ssid; 381 } 382 383 // Detect encoding and convert to UTF-8. 384 std::string country_code; 385 properties.GetStringWithoutPathExpansion( 386 flimflam::kCountryProperty, &country_code); 387 std::string encoding; 388 if (!base::DetectEncoding(ssid, &encoding)) { 389 // TODO(stevenjb): This is currently experimental. If we find a case where 390 // base::DetectEncoding() fails, we need to figure out whether we can use 391 // country_code with ConvertToUtf8(). crbug.com/233267. 392 encoding = country_code; 393 } 394 if (!encoding.empty()) { 395 std::string utf8_ssid; 396 if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { 397 if (utf8_ssid != name) { 398 NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf( 399 "%s: Encoding=%s: %s", name.c_str(), 400 encoding.c_str(), utf8_ssid.c_str())); 401 } 402 return utf8_ssid; 403 } 404 } 405 406 // Unrecognized encoding. Only use raw bytes if name_ is empty. 407 NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf( 408 "%s: Unrecognized Encoding=%s: %s", name.c_str(), 409 encoding.c_str(), ssid.c_str())); 410 if (name.empty() && !ssid.empty()) 411 return ssid; 412 return name; 413} 414 415// static 416bool NetworkState::StateIsConnected(const std::string& connection_state) { 417 return (connection_state == flimflam::kStateReady || 418 connection_state == flimflam::kStateOnline || 419 connection_state == flimflam::kStatePortal); 420} 421 422// static 423bool NetworkState::StateIsConnecting(const std::string& connection_state) { 424 return (connection_state == flimflam::kStateAssociation || 425 connection_state == flimflam::kStateConfiguration || 426 connection_state == flimflam::kStateCarrier); 427} 428 429// static 430std::string NetworkState::IPConfigProperty(const char* key) { 431 return base::StringPrintf("%s.%s", shill::kIPConfigProperty, key); 432} 433 434// static 435bool NetworkState::GetOncSource(const base::Value& ui_data_value, 436 onc::ONCSource* out) { 437 scoped_ptr<NetworkUIData> ui_data(CreateUIDataFromValue(ui_data_value)); 438 if (!ui_data) 439 return false; 440 *out = ui_data->onc_source(); 441 return true; 442} 443 444} // namespace chromeos 445