• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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