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
37 using base::Bind;
38 using base::StringPrintf;
39 using base::Unretained;
40 using std::replace_if;
41 using std::string;
42
43 namespace shill {
44
45 namespace Logging {
46 static auto kModuleLogScope = ScopeLogger::kVPN;
ObjectID(const VPNService * s)47 static string ObjectID(const VPNService* s) { return s->GetRpcIdentifier(); }
48 }
49
50 const char VPNService::kAutoConnNeverConnected[] = "never connected";
51 const char VPNService::kAutoConnVPNAlreadyActive[] = "vpn already active";
52
VPNService(ControlInterface * control,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,VPNDriver * driver)53 VPNService::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
~VPNService()72 VPNService::~VPNService() {}
73
Connect(Error * error,const char * reason)74 void 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
Disconnect(Error * error,const char * reason)92 void 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
GetStorageIdentifier() const98 string VPNService::GetStorageIdentifier() const {
99 return storage_id_;
100 }
101
102 // static
CreateStorageIdentifier(const KeyValueStore & args,Error * error)103 string 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
GetDeviceRpcId(Error * error) const122 string VPNService::GetDeviceRpcId(Error* error) const {
123 error->Populate(Error::kNotSupported);
124 return "/";
125 }
126
Load(StoreInterface * storage)127 bool VPNService::Load(StoreInterface* storage) {
128 return Service::Load(storage) &&
129 driver_->Load(storage, GetStorageIdentifier());
130 }
131
Save(StoreInterface * storage)132 bool VPNService::Save(StoreInterface* storage) {
133 return Service::Save(storage) &&
134 driver_->Save(storage, GetStorageIdentifier(), save_credentials());
135 }
136
Unload()137 bool 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
InitDriverPropertyStore()150 void VPNService::InitDriverPropertyStore() {
151 driver_->InitPropertyStore(mutable_store());
152 }
153
EnableAndRetainAutoConnect()154 void 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
SetConnection(const ConnectionRefPtr & connection)160 void 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
IsAutoConnectable(const char ** reason) const182 bool 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
GetTethering(Error * error) const200 string 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
SetNameProperty(const string & name,Error * error)222 bool 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
GetPhysicalTechnologyProperty(Error * error)248 string 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