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