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