blob: 15dde681771daebebe904db73ee3b3147eff7581 [file] [log] [blame]
Darin Petkov33af05c2012-02-28 10:10:30 +01001// Copyright (c) 2012 The Chromium OS 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 "shill/vpn_service.h"
6
Darin Petkov02867712012-03-12 14:25:05 +01007#include <algorithm>
Darin Petkov33af05c2012-02-28 10:10:30 +01008
Darin Petkov02867712012-03-12 14:25:05 +01009#include <base/stringprintf.h>
10#include <chromeos/dbus/service_constants.h>
11
12#include "shill/key_value_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070013#include "shill/logging.h"
Paul Stewart65512e12012-03-26 18:01:08 -070014#include "shill/manager.h"
Darin Petkov9c6e9812013-03-26 13:49:07 +010015#include "shill/profile.h"
Alex Deymofddc09a2013-07-03 18:41:31 -070016#include "shill/property_accessor.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010017#include "shill/technology.h"
18#include "shill/vpn_driver.h"
Darin Petkovc3505a52013-03-18 15:13:29 +010019#include "shill/vpn_provider.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010020
Darin Petkov5eb05422012-05-11 15:45:25 +020021using base::Bind;
Darin Petkov02867712012-03-12 14:25:05 +010022using base::StringPrintf;
Darin Petkov5eb05422012-05-11 15:45:25 +020023using base::Unretained;
Darin Petkov02867712012-03-12 14:25:05 +010024using std::replace_if;
Darin Petkov33af05c2012-02-28 10:10:30 +010025using std::string;
26
27namespace shill {
28
Darin Petkov79349f02013-01-24 16:18:26 +010029const char VPNService::kAutoConnNeverConnected[] = "never connected";
Darin Petkov4cbff5b2013-01-29 16:29:05 +010030const char VPNService::kAutoConnVPNAlreadyActive[] = "vpn already active";
Darin Petkov79349f02013-01-24 16:18:26 +010031
Darin Petkov79d74c92012-03-07 17:20:32 +010032VPNService::VPNService(ControlInterface *control,
Darin Petkov33af05c2012-02-28 10:10:30 +010033 EventDispatcher *dispatcher,
34 Metrics *metrics,
35 Manager *manager,
36 VPNDriver *driver)
Darin Petkov79d74c92012-03-07 17:20:32 +010037 : Service(control, dispatcher, metrics, manager, Technology::kVPN),
Paul Stewart22807992012-04-11 08:48:31 -070038 driver_(driver) {
mukesh agrawalcbfb34e2013-04-17 19:33:25 -070039 SetConnectable(true);
Darin Petkovcb715292012-04-25 13:04:37 +020040 set_save_credentials(false);
Paul Stewart22807992012-04-11 08:48:31 -070041 mutable_store()->RegisterString(flimflam::kVPNDomainProperty, &vpn_domain_);
Alex Deymofddc09a2013-07-03 18:41:31 -070042 mutable_store()->RegisterDerivedString(
43 kPhysicalTechnologyProperty,
44 StringAccessor(
45 new CustomAccessor<VPNService, string>(
46 this,
47 &VPNService::GetPhysicalTechologyProperty,
48 NULL)));
Paul Stewart22807992012-04-11 08:48:31 -070049}
Darin Petkov33af05c2012-02-28 10:10:30 +010050
51VPNService::~VPNService() {}
52
mukesh agrawaldc7b8442012-09-27 13:48:14 -070053void VPNService::Connect(Error *error, const char *reason) {
mukesh agrawaldba10462013-06-04 11:36:21 -070054 if (IsConnected()) {
55 Error::PopulateAndLog(error, Error::kAlreadyConnected,
56 StringPrintf("VPN service %s already connected.",
57 unique_name().c_str()));
58 return;
59 }
60 if (IsConnecting()) {
61 Error::PopulateAndLog(error, Error::kInProgress,
62 StringPrintf("VPN service %s already connecting.",
63 unique_name().c_str()));
Darin Petkov2f903b32012-04-18 12:56:43 +020064 return;
65 }
mukesh agrawaldc7b8442012-09-27 13:48:14 -070066 Service::Connect(error, reason);
Darin Petkov79d74c92012-03-07 17:20:32 +010067 driver_->Connect(this, error);
Darin Petkov33af05c2012-02-28 10:10:30 +010068}
69
Darin Petkov6aa21872012-03-09 16:10:19 +010070void VPNService::Disconnect(Error *error) {
Darin Petkov457728b2013-01-09 09:49:08 +010071 LOG(INFO) << "Disconnect from service " << unique_name();
Darin Petkov6aa21872012-03-09 16:10:19 +010072 Service::Disconnect(error);
73 driver_->Disconnect();
74}
75
Darin Petkov33af05c2012-02-28 10:10:30 +010076string VPNService::GetStorageIdentifier() const {
Darin Petkov02867712012-03-12 14:25:05 +010077 return storage_id_;
78}
79
80// static
81string VPNService::CreateStorageIdentifier(const KeyValueStore &args,
82 Error *error) {
Darin Petkov7f060332012-03-14 11:46:47 +010083 string host = args.LookupString(flimflam::kProviderHostProperty, "");
Darin Petkov02867712012-03-12 14:25:05 +010084 if (host.empty()) {
85 Error::PopulateAndLog(
86 error, Error::kInvalidProperty, "Missing VPN host.");
87 return "";
88 }
Darin Petkov4e02ba22013-04-02 13:44:08 +020089 string name = args.LookupString(flimflam::kNameProperty, "");
Darin Petkov02867712012-03-12 14:25:05 +010090 if (name.empty()) {
Darin Petkov4e02ba22013-04-02 13:44:08 +020091 Error::PopulateAndLog(error, Error::kNotSupported, "Missing VPN name.");
92 return "";
Darin Petkov02867712012-03-12 14:25:05 +010093 }
94 string id = StringPrintf("vpn_%s_%s", host.c_str(), name.c_str());
95 replace_if(id.begin(), id.end(), &Service::IllegalChar, '_');
96 return id;
Darin Petkov33af05c2012-02-28 10:10:30 +010097}
98
99string VPNService::GetDeviceRpcId(Error *error) {
Darin Petkov33af05c2012-02-28 10:10:30 +0100100 error->Populate(Error::kNotSupported);
101 return "/";
102}
103
Darin Petkovf3c71d72012-03-21 12:32:15 +0100104bool VPNService::Load(StoreInterface *storage) {
105 return Service::Load(storage) &&
106 driver_->Load(storage, GetStorageIdentifier());
107}
108
109bool VPNService::Save(StoreInterface *storage) {
110 return Service::Save(storage) &&
Darin Petkovcb715292012-04-25 13:04:37 +0200111 driver_->Save(storage, GetStorageIdentifier(), save_credentials());
Darin Petkovf3c71d72012-03-21 12:32:15 +0100112}
113
Paul Stewart65512e12012-03-26 18:01:08 -0700114bool VPNService::Unload() {
Darin Petkova0e645e2012-04-25 11:38:59 +0200115 // The base method also disconnects the service.
Paul Stewart65512e12012-03-26 18:01:08 -0700116 Service::Unload();
117
Darin Petkovcb715292012-04-25 13:04:37 +0200118 set_save_credentials(false);
119 driver_->UnloadCredentials();
120
Paul Stewart65512e12012-03-26 18:01:08 -0700121 // Ask the VPN provider to remove us from its list.
122 manager()->vpn_provider()->RemoveService(this);
123
124 return true;
125}
126
Paul Stewartebd38562012-03-23 13:06:40 -0700127void VPNService::InitDriverPropertyStore() {
128 driver_->InitPropertyStore(mutable_store());
129}
130
Darin Petkov1d0080a2012-04-30 17:10:36 +0200131void VPNService::MakeFavorite() {
132 // The base MakeFavorite method also sets auto_connect_ to true
133 // which is not desirable for VPN services.
mukesh agrawalcbfb34e2013-04-17 19:33:25 -0700134 MarkAsFavorite();
Darin Petkov1d0080a2012-04-30 17:10:36 +0200135}
136
Darin Petkov5eb05422012-05-11 15:45:25 +0200137void VPNService::SetConnection(const ConnectionRefPtr &connection) {
138 // Construct the connection binder here rather than in the constructor because
Darin Petkov457728b2013-01-09 09:49:08 +0100139 // there's really no reason to construct a binder if we never connect to this
140 // service. It's safe to use an unretained callback to driver's method because
141 // both the binder and the driver will be destroyed when this service is
142 // destructed.
Darin Petkov5eb05422012-05-11 15:45:25 +0200143 if (!connection_binder_.get()) {
144 connection_binder_.reset(
Darin Petkov457728b2013-01-09 09:49:08 +0100145 new Connection::Binder(unique_name(),
Darin Petkov5eb05422012-05-11 15:45:25 +0200146 Bind(&VPNDriver::OnConnectionDisconnected,
147 Unretained(driver_.get()))));
148 }
149 // Note that |connection_| is a reference-counted pointer and is always set
150 // through this method. This means that the connection binder will not be
151 // notified when the connection is destructed (because we will unbind it first
152 // here when it's set to NULL, or because the binder will already be destroyed
153 // by ~VPNService) -- it will be notified only if the connection disconnects
154 // (e.g., because an underlying connection is destructed).
155 connection_binder_->Attach(connection);
156 Service::SetConnection(connection);
157}
158
Darin Petkov79349f02013-01-24 16:18:26 +0100159bool VPNService::IsAutoConnectable(const char **reason) const {
160 if (!Service::IsAutoConnectable(reason)) {
161 return false;
162 }
163 // Don't auto-connect VPN services that have never connected. This improves
164 // the chances that the VPN service is connectable and avoids dialog popups.
165 if (!has_ever_connected()) {
166 *reason = kAutoConnNeverConnected;
167 return false;
168 }
Darin Petkov4cbff5b2013-01-29 16:29:05 +0100169 // Don't auto-connect a VPN service if another VPN service is already active.
170 if (manager()->vpn_provider()->HasActiveService()) {
171 *reason = kAutoConnVPNAlreadyActive;
172 return false;
173 }
Darin Petkov79349f02013-01-24 16:18:26 +0100174 return true;
175}
176
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700177bool VPNService::SetNameProperty(const string &name, Error *error) {
Darin Petkov9c6e9812013-03-26 13:49:07 +0100178 if (name == friendly_name()) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700179 return false;
Darin Petkov9c6e9812013-03-26 13:49:07 +0100180 }
181 LOG(INFO) << "Renaming service " << unique_name() << ": "
182 << friendly_name() << " -> " << name;
183
184 KeyValueStore *args = driver_->args();
Darin Petkov9c6e9812013-03-26 13:49:07 +0100185 args->SetString(flimflam::kNameProperty, name);
186 string new_storage_id = CreateStorageIdentifier(*args, error);
187 if (new_storage_id.empty()) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700188 return false;
Darin Petkov9c6e9812013-03-26 13:49:07 +0100189 }
190 string old_storage_id = storage_id_;
191 DCHECK_NE(old_storage_id, new_storage_id);
192
193 SetFriendlyName(name);
194
195 // Update the storage identifier before invoking DeleteEntry to prevent it
196 // from unloading this service.
197 storage_id_ = new_storage_id;
198 profile()->DeleteEntry(old_storage_id, NULL);
199 profile()->UpdateService(this);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700200 return true;
Darin Petkov9c6e9812013-03-26 13:49:07 +0100201}
202
Alex Deymofddc09a2013-07-03 18:41:31 -0700203string VPNService::GetPhysicalTechologyProperty(Error *error) {
204 ConnectionRefPtr conn = connection();
205 if (conn)
206 conn = conn->GetCarrierConnection();
207
208 if (!conn) {
209 Error::PopulateAndLog(error,
210 Error::kOperationFailed,
211 Error::GetDefaultMessage(Error::kOperationFailed));
212 return "";
213 }
214
215 return Technology::NameFromIdentifier(conn->technology());
216}
217
Darin Petkov33af05c2012-02-28 10:10:30 +0100218} // namespace shill