Home | History | Annotate | Download | only in network
      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 
     20 namespace {
     21 
     22 const char kErrorUnknown[] = "Unknown";
     23 
     24 bool 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.
     36 std::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 
     55 bool 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 
     84 namespace chromeos {
     85 
     86 NetworkState::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 
     99 NetworkState::~NetworkState() {
    100 }
    101 
    102 bool 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 == IPConfigProperty(
    136       shill::kWebProxyAutoDiscoveryUrlProperty)) {
    137     std::string url_string;
    138     if (!GetStringValue(key, value, &url_string))
    139       return false;
    140     if (url_string.empty()) {
    141       web_proxy_auto_discovery_url_ = GURL();
    142     } else {
    143       GURL gurl(url_string);
    144       if (!gurl.is_valid()) {
    145         web_proxy_auto_discovery_url_ = gurl;
    146       } else {
    147         NET_LOG_ERROR("Invalid WebProxyAutoDiscoveryUrl: " + url_string,
    148                       path());
    149         web_proxy_auto_discovery_url_ = GURL();
    150       }
    151     }
    152     return true;
    153   } else if (key == flimflam::kActivationStateProperty) {
    154     return GetStringValue(key, value, &activation_state_);
    155   } else if (key == flimflam::kRoamingStateProperty) {
    156     return GetStringValue(key, value, &roaming_);
    157   } else if (key == flimflam::kSecurityProperty) {
    158     return GetStringValue(key, value, &security_);
    159   } else if (key == flimflam::kAutoConnectProperty) {
    160     return GetBooleanValue(key, value, &auto_connect_);
    161   } else if (key == flimflam::kFavoriteProperty) {
    162     return GetBooleanValue(key, value, &favorite_);
    163   } else if (key == flimflam::kPriorityProperty) {
    164     return GetIntegerValue(key, value, &priority_);
    165   } else if (key == flimflam::kProxyConfigProperty) {
    166     std::string proxy_config_str;
    167     if (!value.GetAsString(&proxy_config_str)) {
    168       NET_LOG_ERROR("Failed to parse " + key, path());
    169       return false;
    170     }
    171 
    172     proxy_config_.Clear();
    173     if (proxy_config_str.empty())
    174       return true;
    175 
    176     scoped_ptr<base::DictionaryValue> proxy_config_dict(
    177         onc::ReadDictionaryFromJson(proxy_config_str));
    178     if (proxy_config_dict) {
    179       // Warning: The DictionaryValue returned from
    180       // ReadDictionaryFromJson/JSONParser is an optimized derived class that
    181       // doesn't allow releasing ownership of nested values. A Swap in the wrong
    182       // order leads to memory access errors.
    183       proxy_config_.MergeDictionary(proxy_config_dict.get());
    184     } else {
    185       NET_LOG_ERROR("Failed to parse " + key, path());
    186     }
    187     return true;
    188   } else if (key == flimflam::kUIDataProperty) {
    189     if (!GetUIDataFromValue(value, &ui_data_)) {
    190       NET_LOG_ERROR("Failed to parse " + key, path());
    191       return false;
    192     }
    193     return true;
    194   } else if (key == flimflam::kNetworkTechnologyProperty) {
    195     return GetStringValue(key, value, &network_technology_);
    196   } else if (key == flimflam::kDeviceProperty) {
    197     return GetStringValue(key, value, &device_path_);
    198   } else if (key == flimflam::kGuidProperty) {
    199     return GetStringValue(key, value, &guid_);
    200   } else if (key == flimflam::kProfileProperty) {
    201     return GetStringValue(key, value, &profile_path_);
    202   } else if (key == shill::kActivateOverNonCellularNetworkProperty) {
    203     return GetBooleanValue(key, value, &activate_over_non_cellular_networks_);
    204   } else if (key == shill::kOutOfCreditsProperty) {
    205     return GetBooleanValue(key, value, &cellular_out_of_credits_);
    206   } else if (key == flimflam::kUsageURLProperty) {
    207     return GetStringValue(key, value, &usage_url_);
    208   } else if (key == flimflam::kPaymentPortalProperty) {
    209     const DictionaryValue* dict;
    210     if (!value.GetAsDictionary(&dict))
    211       return false;
    212     if (!dict->GetStringWithoutPathExpansion(
    213             flimflam::kPaymentPortalURL, &payment_url_) ||
    214         !dict->GetStringWithoutPathExpansion(
    215             flimflam::kPaymentPortalMethod, &post_method_) ||
    216         !dict->GetStringWithoutPathExpansion(
    217             flimflam::kPaymentPortalPostData, &post_data_)) {
    218       return false;
    219     }
    220     return true;
    221   }
    222   return false;
    223 }
    224 
    225 bool NetworkState::InitialPropertiesReceived(
    226     const base::DictionaryValue& properties) {
    227   NET_LOG_DEBUG("InitialPropertiesReceived", path());
    228   bool changed = UpdateName(properties);
    229   bool had_ca_cert_nss = has_ca_cert_nss_;
    230   has_ca_cert_nss_ = IsCaCertNssSet(properties);
    231   changed |= had_ca_cert_nss != has_ca_cert_nss_;
    232   return changed;
    233 }
    234 
    235 void NetworkState::GetProperties(base::DictionaryValue* dictionary) const {
    236   // Keep care that these properties are the same as in |PropertyChanged|.
    237   dictionary->SetStringWithoutPathExpansion(flimflam::kNameProperty, name());
    238   dictionary->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type());
    239   dictionary->SetIntegerWithoutPathExpansion(flimflam::kSignalStrengthProperty,
    240                                              signal_strength_);
    241   dictionary->SetStringWithoutPathExpansion(flimflam::kStateProperty,
    242                                             connection_state_);
    243   dictionary->SetBooleanWithoutPathExpansion(flimflam::kConnectableProperty,
    244                                              connectable_);
    245 
    246   dictionary->SetStringWithoutPathExpansion(flimflam::kErrorProperty,
    247                                             error_);
    248   dictionary->SetStringWithoutPathExpansion(shill::kErrorDetailsProperty,
    249                                             error_details_);
    250 
    251   // IPConfig properties
    252   base::DictionaryValue* ipconfig_properties = new base::DictionaryValue;
    253   ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kAddressProperty,
    254                                                      ip_address_);
    255   ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kGatewayProperty,
    256                                                      gateway_);
    257   base::ListValue* name_servers = new base::ListValue;
    258   name_servers->AppendStrings(dns_servers_);
    259   ipconfig_properties->SetWithoutPathExpansion(flimflam::kNameServersProperty,
    260                                                name_servers);
    261   ipconfig_properties->SetIntegerWithoutPathExpansion(
    262       flimflam::kPrefixlenProperty, prefix_length_);
    263   ipconfig_properties->SetStringWithoutPathExpansion(
    264       shill::kWebProxyAutoDiscoveryUrlProperty,
    265       web_proxy_auto_discovery_url_.spec());
    266   dictionary->SetWithoutPathExpansion(shill::kIPConfigProperty,
    267                                       ipconfig_properties);
    268 
    269   dictionary->SetStringWithoutPathExpansion(flimflam::kActivationStateProperty,
    270                                             activation_state_);
    271   dictionary->SetStringWithoutPathExpansion(flimflam::kRoamingStateProperty,
    272                                             roaming_);
    273   dictionary->SetStringWithoutPathExpansion(flimflam::kSecurityProperty,
    274                                             security_);
    275   dictionary->SetBooleanWithoutPathExpansion(flimflam::kAutoConnectProperty,
    276                                              auto_connect_);
    277   dictionary->SetBooleanWithoutPathExpansion(flimflam::kFavoriteProperty,
    278                                              favorite_);
    279   dictionary->SetIntegerWithoutPathExpansion(flimflam::kPriorityProperty,
    280                                              priority_);
    281   // Proxy config and ONC source are intentionally omitted: These properties are
    282   // placed in NetworkState to transition ProxyConfigServiceImpl from
    283   // NetworkLibrary to the new network stack. The networking extension API
    284   // shouldn't depend on this member. Once ManagedNetworkConfigurationHandler
    285   // is used instead of NetworkLibrary, we can remove them again.
    286   dictionary->SetStringWithoutPathExpansion(
    287       flimflam::kNetworkTechnologyProperty,
    288       network_technology_);
    289   dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty,
    290                                             device_path_);
    291   dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid_);
    292   dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty,
    293                                             profile_path_);
    294   dictionary->SetBooleanWithoutPathExpansion(
    295       shill::kActivateOverNonCellularNetworkProperty,
    296       activate_over_non_cellular_networks_);
    297   dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty,
    298                                              cellular_out_of_credits_);
    299   base::DictionaryValue* payment_portal_properties = new DictionaryValue;
    300   payment_portal_properties->SetStringWithoutPathExpansion(
    301       flimflam::kPaymentPortalURL,
    302       payment_url_);
    303   payment_portal_properties->SetStringWithoutPathExpansion(
    304       flimflam::kPaymentPortalMethod,
    305       post_method_);
    306   payment_portal_properties->SetStringWithoutPathExpansion(
    307       flimflam::kPaymentPortalPostData,
    308       post_data_);
    309   dictionary->SetWithoutPathExpansion(flimflam::kPaymentPortalProperty,
    310                                       payment_portal_properties);
    311 }
    312 
    313 bool NetworkState::IsConnectedState() const {
    314   return StateIsConnected(connection_state_);
    315 }
    316 
    317 bool NetworkState::IsConnectingState() const {
    318   return StateIsConnecting(connection_state_);
    319 }
    320 
    321 bool NetworkState::IsManaged() const {
    322   return ui_data_.onc_source() == onc::ONC_SOURCE_DEVICE_POLICY ||
    323          ui_data_.onc_source() == onc::ONC_SOURCE_USER_POLICY;
    324 }
    325 
    326 bool NetworkState::IsPrivate() const {
    327   return !profile_path_.empty() &&
    328       profile_path_ != NetworkProfileHandler::kSharedProfilePath;
    329 }
    330 
    331 std::string NetworkState::GetDnsServersAsString() const {
    332   std::string result;
    333   for (size_t i = 0; i < dns_servers_.size(); ++i) {
    334     if (i != 0)
    335       result += ",";
    336     result += dns_servers_[i];
    337   }
    338   return result;
    339 }
    340 
    341 std::string NetworkState::GetNetmask() const {
    342   return network_util::PrefixLengthToNetmask(prefix_length_);
    343 }
    344 
    345 bool NetworkState::UpdateName(const base::DictionaryValue& properties) {
    346   std::string updated_name = GetNameFromProperties(path(), properties);
    347   if (updated_name != name()) {
    348     set_name(updated_name);
    349     return true;
    350   }
    351   return false;
    352 }
    353 
    354 // static
    355 std::string NetworkState::GetNameFromProperties(
    356     const std::string& service_path,
    357     const base::DictionaryValue& properties) {
    358   std::string name;
    359   properties.GetStringWithoutPathExpansion(flimflam::kNameProperty, &name);
    360 
    361   std::string hex_ssid;
    362   properties.GetStringWithoutPathExpansion(flimflam::kWifiHexSsid, &hex_ssid);
    363 
    364   if (hex_ssid.empty()) {
    365     // Validate name for UTF8.
    366     std::string valid_ssid = ValidateUTF8(name);
    367     if (valid_ssid != name) {
    368       NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
    369           "%s: UTF8: %s", service_path.c_str(), valid_ssid.c_str()));
    370     }
    371     return valid_ssid;
    372   }
    373 
    374   std::string ssid;
    375   std::vector<uint8> raw_ssid_bytes;
    376   if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
    377     ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
    378     NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
    379         "%s: %s, SSID: %s", service_path.c_str(),
    380         hex_ssid.c_str(), ssid.c_str()));
    381   } else {
    382     NET_LOG_ERROR("GetNameFromProperties",
    383                   base::StringPrintf("%s: Error processing: %s",
    384                                      service_path.c_str(), hex_ssid.c_str()));
    385     return name;
    386   }
    387 
    388   if (IsStringUTF8(ssid)) {
    389     if (ssid != name) {
    390       NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
    391           "%s: UTF8: %s", service_path.c_str(), ssid.c_str()));
    392     }
    393     return ssid;
    394   }
    395 
    396   // Detect encoding and convert to UTF-8.
    397   std::string country_code;
    398   properties.GetStringWithoutPathExpansion(
    399       flimflam::kCountryProperty, &country_code);
    400   std::string encoding;
    401   if (!base::DetectEncoding(ssid, &encoding)) {
    402     // TODO(stevenjb): This is currently experimental. If we find a case where
    403     // base::DetectEncoding() fails, we need to figure out whether we can use
    404     // country_code with ConvertToUtf8(). crbug.com/233267.
    405     encoding = country_code;
    406   }
    407   if (!encoding.empty()) {
    408     std::string utf8_ssid;
    409     if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
    410       if (utf8_ssid != name) {
    411         NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
    412             "%s: Encoding=%s: %s", service_path.c_str(),
    413             encoding.c_str(), utf8_ssid.c_str()));
    414       }
    415       return utf8_ssid;
    416     }
    417   }
    418 
    419   // Unrecognized encoding. Only use raw bytes if name_ is empty.
    420   NET_LOG_DEBUG("GetNameFromProperties", base::StringPrintf(
    421       "%s: Unrecognized Encoding=%s: %s", service_path.c_str(),
    422       encoding.c_str(), ssid.c_str()));
    423   if (name.empty() && !ssid.empty())
    424     return ssid;
    425   return name;
    426 }
    427 
    428 // static
    429 bool NetworkState::StateIsConnected(const std::string& connection_state) {
    430   return (connection_state == flimflam::kStateReady ||
    431           connection_state == flimflam::kStateOnline ||
    432           connection_state == flimflam::kStatePortal);
    433 }
    434 
    435 // static
    436 bool NetworkState::StateIsConnecting(const std::string& connection_state) {
    437   return (connection_state == flimflam::kStateAssociation ||
    438           connection_state == flimflam::kStateConfiguration ||
    439           connection_state == flimflam::kStateCarrier);
    440 }
    441 
    442 // static
    443 std::string NetworkState::IPConfigProperty(const char* key) {
    444   return base::StringPrintf("%s.%s", shill::kIPConfigProperty, key);
    445 }
    446 
    447 // static
    448 bool NetworkState::GetUIDataFromValue(const base::Value& ui_data_value,
    449                                       NetworkUIData* out) {
    450   std::string ui_data_str;
    451   if (!ui_data_value.GetAsString(&ui_data_str))
    452     return false;
    453   if (ui_data_str.empty()) {
    454     *out = NetworkUIData();
    455     return true;
    456   }
    457   scoped_ptr<base::DictionaryValue> ui_data_dict(
    458       chromeos::onc::ReadDictionaryFromJson(ui_data_str));
    459   if (!ui_data_dict)
    460     return false;
    461   *out = NetworkUIData(*ui_data_dict);
    462   return true;
    463 }
    464 
    465 }  // namespace chromeos
    466