1//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/vpn/vpn_service.h"
18
19#include <algorithm>
20
21#include <base/strings/stringprintf.h>
22#if defined(__ANDROID__)
23#include <dbus/service_constants.h>
24#else
25#include <chromeos/dbus/service_constants.h>
26#endif  // __ANDROID__
27
28#include "shill/key_value_store.h"
29#include "shill/logging.h"
30#include "shill/manager.h"
31#include "shill/profile.h"
32#include "shill/property_accessor.h"
33#include "shill/technology.h"
34#include "shill/vpn/vpn_driver.h"
35#include "shill/vpn/vpn_provider.h"
36
37using base::Bind;
38using base::StringPrintf;
39using base::Unretained;
40using std::replace_if;
41using std::string;
42
43namespace shill {
44
45namespace Logging {
46static auto kModuleLogScope = ScopeLogger::kVPN;
47static string ObjectID(const VPNService* s) { return s->GetRpcIdentifier(); }
48}
49
50const char VPNService::kAutoConnNeverConnected[] = "never connected";
51const char VPNService::kAutoConnVPNAlreadyActive[] = "vpn already active";
52
53VPNService::VPNService(ControlInterface* control,
54                       EventDispatcher* dispatcher,
55                       Metrics* metrics,
56                       Manager* manager,
57                       VPNDriver* driver)
58    : Service(control, dispatcher, metrics, manager, Technology::kVPN),
59      driver_(driver) {
60  SetConnectable(true);
61  set_save_credentials(false);
62  mutable_store()->RegisterString(kVPNDomainProperty, &vpn_domain_);
63  mutable_store()->RegisterDerivedString(
64          kPhysicalTechnologyProperty,
65          StringAccessor(
66              new CustomAccessor<VPNService, string>(
67                  this,
68                  &VPNService::GetPhysicalTechnologyProperty,
69                  nullptr)));
70}
71
72VPNService::~VPNService() {}
73
74void VPNService::Connect(Error* error, const char* reason) {
75  if (IsConnected()) {
76    Error::PopulateAndLog(FROM_HERE, error, Error::kAlreadyConnected,
77                          StringPrintf("VPN service %s already connected.",
78                                       unique_name().c_str()));
79    return;
80  }
81  if (IsConnecting()) {
82    Error::PopulateAndLog(FROM_HERE, error, Error::kInProgress,
83                          StringPrintf("VPN service %s already connecting.",
84                                       unique_name().c_str()));
85    return;
86  }
87  manager()->vpn_provider()->DisconnectAll();
88  Service::Connect(error, reason);
89  driver_->Connect(this, error);
90}
91
92void VPNService::Disconnect(Error* error, const char* reason) {
93  SLOG(this, 1) << "Disconnect from service " << unique_name();
94  Service::Disconnect(error, reason);
95  driver_->Disconnect();
96}
97
98string VPNService::GetStorageIdentifier() const {
99  return storage_id_;
100}
101
102// static
103string VPNService::CreateStorageIdentifier(const KeyValueStore& args,
104                                           Error* error) {
105  string host = args.LookupString(kProviderHostProperty, "");
106  if (host.empty()) {
107    Error::PopulateAndLog(
108        FROM_HERE, error, Error::kInvalidProperty, "Missing VPN host.");
109    return "";
110  }
111  string name = args.LookupString(kNameProperty, "");
112  if (name.empty()) {
113    Error::PopulateAndLog(
114        FROM_HERE, error, Error::kNotSupported, "Missing VPN name.");
115    return "";
116  }
117  string id = StringPrintf("vpn_%s_%s", host.c_str(), name.c_str());
118  replace_if(id.begin(), id.end(), &Service::IllegalChar, '_');
119  return id;
120}
121
122string VPNService::GetDeviceRpcId(Error* error) const {
123  error->Populate(Error::kNotSupported);
124  return "/";
125}
126
127bool VPNService::Load(StoreInterface* storage) {
128  return Service::Load(storage) &&
129      driver_->Load(storage, GetStorageIdentifier());
130}
131
132bool VPNService::Save(StoreInterface* storage) {
133  return Service::Save(storage) &&
134      driver_->Save(storage, GetStorageIdentifier(), save_credentials());
135}
136
137bool VPNService::Unload() {
138  // The base method also disconnects the service.
139  Service::Unload();
140
141  set_save_credentials(false);
142  driver_->UnloadCredentials();
143
144  // Ask the VPN provider to remove us from its list.
145  manager()->vpn_provider()->RemoveService(this);
146
147  return true;
148}
149
150void VPNService::InitDriverPropertyStore() {
151  driver_->InitPropertyStore(mutable_store());
152}
153
154void VPNService::EnableAndRetainAutoConnect() {
155  // The base EnableAndRetainAutoConnect method also sets auto_connect_ to true
156  // which is not desirable for VPN services.
157  RetainAutoConnect();
158}
159
160void VPNService::SetConnection(const ConnectionRefPtr& connection) {
161  // Construct the connection binder here rather than in the constructor because
162  // there's really no reason to construct a binder if we never connect to this
163  // service. It's safe to use an unretained callback to driver's method because
164  // both the binder and the driver will be destroyed when this service is
165  // destructed.
166  if (!connection_binder_.get()) {
167    connection_binder_.reset(
168        new Connection::Binder(unique_name(),
169                               Bind(&VPNDriver::OnConnectionDisconnected,
170                                    Unretained(driver_.get()))));
171  }
172  // Note that |connection_| is a reference-counted pointer and is always set
173  // through this method. This means that the connection binder will not be
174  // notified when the connection is destructed (because we will unbind it first
175  // here when it's set to NULL, or because the binder will already be destroyed
176  // by ~VPNService) -- it will be notified only if the connection disconnects
177  // (e.g., because an underlying connection is destructed).
178  connection_binder_->Attach(connection);
179  Service::SetConnection(connection);
180}
181
182bool VPNService::IsAutoConnectable(const char** reason) const {
183  if (!Service::IsAutoConnectable(reason)) {
184    return false;
185  }
186  // Don't auto-connect VPN services that have never connected. This improves
187  // the chances that the VPN service is connectable and avoids dialog popups.
188  if (!has_ever_connected()) {
189    *reason = kAutoConnNeverConnected;
190    return false;
191  }
192  // Don't auto-connect a VPN service if another VPN service is already active.
193  if (manager()->vpn_provider()->HasActiveService()) {
194    *reason = kAutoConnVPNAlreadyActive;
195    return false;
196  }
197  return true;
198}
199
200string VPNService::GetTethering(Error* error) const {
201  ConnectionRefPtr conn = connection();
202  if (conn)
203    conn = conn->GetCarrierConnection();
204
205  string tethering;
206  if (conn) {
207    tethering = conn->tethering();
208    if (!tethering.empty()) {
209      return tethering;
210    }
211    // The underlying service may not have a Tethering property.  This is
212    // not strictly an error, so we don't print an error message.  Populating
213    // an error here just serves to propagate the lack of a property in
214    // GetProperties().
215    error->Populate(Error::kNotSupported);
216  } else {
217    error->Populate(Error::kOperationFailed);
218  }
219  return "";
220}
221
222bool VPNService::SetNameProperty(const string& name, Error* error) {
223  if (name == friendly_name()) {
224    return false;
225  }
226  LOG(INFO) << "Renaming service " << unique_name() << ": "
227            << friendly_name() << " -> " << name;
228
229  KeyValueStore* args = driver_->args();
230  args->SetString(kNameProperty, name);
231  string new_storage_id = CreateStorageIdentifier(*args, error);
232  if (new_storage_id.empty()) {
233    return false;
234  }
235  string old_storage_id = storage_id_;
236  DCHECK_NE(old_storage_id, new_storage_id);
237
238  SetFriendlyName(name);
239
240  // Update the storage identifier before invoking DeleteEntry to prevent it
241  // from unloading this service.
242  storage_id_ = new_storage_id;
243  profile()->DeleteEntry(old_storage_id, nullptr);
244  profile()->UpdateService(this);
245  return true;
246}
247
248string VPNService::GetPhysicalTechnologyProperty(Error* error) {
249  ConnectionRefPtr conn = connection();
250  if (conn)
251    conn = conn->GetCarrierConnection();
252
253  if (!conn) {
254    error->Populate(Error::kOperationFailed);
255    return "";
256  }
257
258  return Technology::NameFromIdentifier(conn->technology());
259}
260
261}  // namespace shill
262