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