1 // Copyright 2019 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/components/sync_wifi/local_network_collector_impl.h"
6 
7 #include "base/guid.h"
8 #include "chromeos/components/sync_wifi/network_eligibility_checker.h"
9 #include "chromeos/components/sync_wifi/network_identifier.h"
10 #include "chromeos/components/sync_wifi/network_type_conversions.h"
11 #include "chromeos/dbus/shill/shill_service_client.h"
12 #include "chromeos/network/network_event_log.h"
13 #include "chromeos/network/network_handler.h"
14 #include "chromeos/network/network_metadata_store.h"
15 #include "chromeos/network/network_state.h"
16 #include "chromeos/network/network_state_handler.h"
17 #include "chromeos/services/network_config/public/mojom/network_types.mojom-shared.h"
18 #include "components/device_event_log/device_event_log.h"
19 #include "components/sync/protocol/wifi_configuration_specifics.pb.h"
20 #include "dbus/object_path.h"
21 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
22 
23 namespace chromeos {
24 
25 namespace sync_wifi {
26 
27 namespace {
28 
GetServicePathForGuid(const std::string & guid)29 dbus::ObjectPath GetServicePathForGuid(const std::string& guid) {
30   const NetworkState* state =
31       NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
32           guid);
33   if (!state) {
34     return dbus::ObjectPath();
35   }
36 
37   return dbus::ObjectPath(state->path());
38 }
39 
40 }  // namespace
41 
LocalNetworkCollectorImpl(network_config::mojom::CrosNetworkConfig * cros_network_config)42 LocalNetworkCollectorImpl::LocalNetworkCollectorImpl(
43     network_config::mojom::CrosNetworkConfig* cros_network_config)
44     : cros_network_config_(cros_network_config) {
45   cros_network_config_->AddObserver(
46       cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
47 
48   // Load the current list of networks.
49   OnNetworkStateListChanged();
50 }
51 
52 LocalNetworkCollectorImpl::~LocalNetworkCollectorImpl() = default;
53 
GetAllSyncableNetworks(ProtoListCallback callback)54 void LocalNetworkCollectorImpl::GetAllSyncableNetworks(
55     ProtoListCallback callback) {
56   std::string request_guid = InitializeRequest();
57   request_guid_to_list_callback_[request_guid] = std::move(callback);
58 
59   int count = 0;
60   for (const network_config::mojom::NetworkStatePropertiesPtr& network :
61        mojo_networks_) {
62     if (!IsEligible(network)) {
63       continue;
64     }
65 
66     request_guid_to_in_flight_networks_[request_guid].insert(
67         NetworkIdentifier::FromMojoNetwork(network));
68     StartGetNetworkDetails(network.get(), request_guid);
69     count++;
70   }
71 
72   if (!count) {
73     OnRequestFinished(request_guid);
74   }
75 }
76 
GetSyncableNetwork(const std::string & guid,ProtoCallback callback)77 void LocalNetworkCollectorImpl::GetSyncableNetwork(const std::string& guid,
78                                                    ProtoCallback callback) {
79   const network_config::mojom::NetworkStateProperties* network = nullptr;
80   for (const network_config::mojom::NetworkStatePropertiesPtr& n :
81        mojo_networks_) {
82     if (n->guid == guid) {
83       if (IsEligible(n)) {
84         network = n.get();
85       }
86 
87       break;
88     }
89   }
90 
91   if (!network) {
92     std::move(callback).Run(base::nullopt);
93     return;
94   }
95 
96   std::string request_guid = InitializeRequest();
97   request_guid_to_single_callback_[request_guid] = std::move(callback);
98 
99   StartGetNetworkDetails(network, request_guid);
100 }
101 
102 base::Optional<NetworkIdentifier>
GetNetworkIdentifierFromGuid(const std::string & guid)103 LocalNetworkCollectorImpl::GetNetworkIdentifierFromGuid(
104     const std::string& guid) {
105   for (const network_config::mojom::NetworkStatePropertiesPtr& network :
106        mojo_networks_) {
107     if (network->guid == guid) {
108       return NetworkIdentifier::FromMojoNetwork(network);
109     }
110   }
111   return base::nullopt;
112 }
113 
SetNetworkMetadataStore(base::WeakPtr<NetworkMetadataStore> network_metadata_store)114 void LocalNetworkCollectorImpl::SetNetworkMetadataStore(
115     base::WeakPtr<NetworkMetadataStore> network_metadata_store) {
116   network_metadata_store_ = network_metadata_store;
117 }
118 
InitializeRequest()119 std::string LocalNetworkCollectorImpl::InitializeRequest() {
120   std::string request_guid = base::GenerateGUID();
121   request_guid_to_complete_protos_[request_guid] =
122       std::vector<sync_pb::WifiConfigurationSpecifics>();
123   request_guid_to_in_flight_networks_[request_guid] =
124       base::flat_set<NetworkIdentifier>();
125   return request_guid;
126 }
127 
IsEligible(const network_config::mojom::NetworkStatePropertiesPtr & network)128 bool LocalNetworkCollectorImpl::IsEligible(
129     const network_config::mojom::NetworkStatePropertiesPtr& network) {
130   if (!network || network->type != network_config::mojom::NetworkType::kWiFi) {
131     return false;
132   }
133 
134   const network_config::mojom::WiFiStatePropertiesPtr& wifi_properties =
135       network->type_state->get_wifi();
136   return IsEligibleForSync(network->guid, network->connectable,
137                            wifi_properties->security, network->source,
138                            /*log_result=*/true);
139 }
140 
StartGetNetworkDetails(const network_config::mojom::NetworkStateProperties * network,const std::string & request_guid)141 void LocalNetworkCollectorImpl::StartGetNetworkDetails(
142     const network_config::mojom::NetworkStateProperties* network,
143     const std::string& request_guid) {
144   sync_pb::WifiConfigurationSpecifics proto;
145   proto.set_hex_ssid(network->type_state->get_wifi()->hex_ssid);
146   proto.set_security_type(
147       SecurityTypeProtoFromMojo(network->type_state->get_wifi()->security));
148   base::TimeDelta timestamp =
149       network_metadata_store_->GetLastConnectedTimestamp(network->guid);
150   proto.set_last_connected_timestamp(timestamp.InMilliseconds());
151   cros_network_config_->GetManagedProperties(
152       network->guid,
153       base::BindOnce(&LocalNetworkCollectorImpl::OnGetManagedPropertiesResult,
154                      weak_ptr_factory_.GetWeakPtr(), proto, request_guid));
155 }
156 
OnGetManagedPropertiesResult(sync_pb::WifiConfigurationSpecifics proto,const std::string & request_guid,network_config::mojom::ManagedPropertiesPtr properties)157 void LocalNetworkCollectorImpl::OnGetManagedPropertiesResult(
158     sync_pb::WifiConfigurationSpecifics proto,
159     const std::string& request_guid,
160     network_config::mojom::ManagedPropertiesPtr properties) {
161   if (!properties) {
162     NET_LOG(ERROR) << "GetManagedProperties failed.";
163     OnNetworkFinished(NetworkIdentifier::FromProto(proto), request_guid);
164     return;
165   }
166   proto.set_automatically_connect(AutomaticallyConnectProtoFromMojo(
167       properties->type_properties->get_wifi()->auto_connect));
168   proto.set_is_preferred(IsPreferredProtoFromMojo(properties->priority));
169 
170   if (properties->metered) {
171     proto.set_metered(
172         properties->metered->active_value
173             ? sync_pb::
174                   WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_YES
175             : sync_pb::
176                   WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_NO);
177   }
178 
179   bool is_proxy_modified =
180       network_metadata_store_->GetIsFieldExternallyModified(
181           properties->guid, shill::kProxyConfigProperty);
182   sync_pb::WifiConfigurationSpecifics_ProxyConfiguration proxy_config =
183       ProxyConfigurationProtoFromMojo(properties->proxy_settings,
184                                       /*is_unspecified=*/is_proxy_modified);
185   proto.mutable_proxy_configuration()->CopyFrom(proxy_config);
186 
187   bool is_dns_externally_modified =
188       network_metadata_store_->GetIsFieldExternallyModified(
189           properties->guid, shill::kNameServersProperty);
190   if (properties->static_ip_config &&
191       properties->static_ip_config->name_servers &&
192       (properties->source == network_config::mojom::OncSource::kUser ||
193        !is_dns_externally_modified)) {
194     proto.set_dns_option(
195         sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_CUSTOM);
196     for (const std::string& nameserver :
197          properties->static_ip_config->name_servers->active_value) {
198       proto.add_custom_dns(nameserver);
199     }
200   } else if (properties->source == network_config::mojom::OncSource::kDevice &&
201              is_dns_externally_modified) {
202     proto.set_dns_option(
203         sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_UNSPECIFIED);
204   } else {
205     proto.set_dns_option(
206         sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_DEFAULT_DHCP);
207   }
208 
209   ShillServiceClient::Get()->GetWiFiPassphrase(
210       GetServicePathForGuid(properties->guid),
211       base::BindOnce(&LocalNetworkCollectorImpl::OnGetWiFiPassphraseResult,
212                      weak_ptr_factory_.GetWeakPtr(), proto, request_guid),
213       base::BindOnce(&LocalNetworkCollectorImpl::OnGetWiFiPassphraseError,
214                      weak_ptr_factory_.GetWeakPtr(),
215                      NetworkIdentifier::FromProto(proto), request_guid));
216 }
217 
OnGetWiFiPassphraseResult(sync_pb::WifiConfigurationSpecifics proto,const std::string & request_guid,const std::string & passphrase)218 void LocalNetworkCollectorImpl::OnGetWiFiPassphraseResult(
219     sync_pb::WifiConfigurationSpecifics proto,
220     const std::string& request_guid,
221     const std::string& passphrase) {
222   proto.set_passphrase(passphrase);
223   NetworkIdentifier id = NetworkIdentifier::FromProto(proto);
224   request_guid_to_complete_protos_[request_guid].push_back(std::move(proto));
225   OnNetworkFinished(id, request_guid);
226 }
227 
OnGetWiFiPassphraseError(const NetworkIdentifier & id,const std::string & request_guid,const std::string & error_name,const std::string & error_message)228 void LocalNetworkCollectorImpl::OnGetWiFiPassphraseError(
229     const NetworkIdentifier& id,
230     const std::string& request_guid,
231     const std::string& error_name,
232     const std::string& error_message) {
233   NET_LOG(ERROR) << "Failed to get passphrase for " << id.SerializeToString()
234                  << " Error: " << error_name << " Details: " << error_message;
235 
236   OnNetworkFinished(id, request_guid);
237 }
238 
OnNetworkFinished(const NetworkIdentifier & id,const std::string & request_guid)239 void LocalNetworkCollectorImpl::OnNetworkFinished(
240     const NetworkIdentifier& id,
241     const std::string& request_guid) {
242   request_guid_to_in_flight_networks_[request_guid].erase(id);
243 
244   if (request_guid_to_in_flight_networks_[request_guid].empty()) {
245     OnRequestFinished(request_guid);
246   }
247 }
248 
OnRequestFinished(const std::string & request_guid)249 void LocalNetworkCollectorImpl::OnRequestFinished(
250     const std::string& request_guid) {
251   DCHECK(request_guid_to_in_flight_networks_[request_guid].empty());
252 
253   if (request_guid_to_single_callback_[request_guid]) {
254     std::vector<sync_pb::WifiConfigurationSpecifics>& list =
255         request_guid_to_complete_protos_[request_guid];
256     DCHECK(list.size() <= 1);
257     base::Optional<sync_pb::WifiConfigurationSpecifics> result;
258     if (list.size() == 1) {
259       result = list[0];
260     }
261     std::move(request_guid_to_single_callback_[request_guid]).Run(result);
262     request_guid_to_single_callback_.erase(request_guid);
263   } else {
264     ProtoListCallback callback =
265         std::move(request_guid_to_list_callback_[request_guid]);
266     DCHECK(callback);
267     std::move(callback).Run(request_guid_to_complete_protos_[request_guid]);
268     request_guid_to_list_callback_.erase(request_guid);
269   }
270 
271   request_guid_to_complete_protos_.erase(request_guid);
272   request_guid_to_in_flight_networks_.erase(request_guid);
273 }
274 
OnNetworkStateListChanged()275 void LocalNetworkCollectorImpl::OnNetworkStateListChanged() {
276   cros_network_config_->GetNetworkStateList(
277       network_config::mojom::NetworkFilter::New(
278           network_config::mojom::FilterType::kConfigured,
279           network_config::mojom::NetworkType::kWiFi,
280           /*limit=*/0),
281       base::BindOnce(&LocalNetworkCollectorImpl::OnGetNetworkList,
282                      weak_ptr_factory_.GetWeakPtr()));
283 }
284 
OnGetNetworkList(std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks)285 void LocalNetworkCollectorImpl::OnGetNetworkList(
286     std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
287   mojo_networks_ = std::move(networks);
288 }
289 
290 }  // namespace sync_wifi
291 
292 }  // namespace chromeos
293