// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/vpn/vpn_service.h" #include <algorithm> #include <base/strings/stringprintf.h> #if defined(__ANDROID__) #include <dbus/service_constants.h> #else #include <chromeos/dbus/service_constants.h> #endif // __ANDROID__ #include "shill/key_value_store.h" #include "shill/logging.h" #include "shill/manager.h" #include "shill/profile.h" #include "shill/property_accessor.h" #include "shill/technology.h" #include "shill/vpn/vpn_driver.h" #include "shill/vpn/vpn_provider.h" using base::Bind; using base::StringPrintf; using base::Unretained; using std::replace_if; using std::string; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kVPN; static string ObjectID(const VPNService* s) { return s->GetRpcIdentifier(); } } const char VPNService::kAutoConnNeverConnected[] = "never connected"; const char VPNService::kAutoConnVPNAlreadyActive[] = "vpn already active"; VPNService::VPNService(ControlInterface* control, EventDispatcher* dispatcher, Metrics* metrics, Manager* manager, VPNDriver* driver) : Service(control, dispatcher, metrics, manager, Technology::kVPN), driver_(driver) { SetConnectable(true); set_save_credentials(false); mutable_store()->RegisterString(kVPNDomainProperty, &vpn_domain_); mutable_store()->RegisterDerivedString( kPhysicalTechnologyProperty, StringAccessor( new CustomAccessor<VPNService, string>( this, &VPNService::GetPhysicalTechnologyProperty, nullptr))); } VPNService::~VPNService() {} void VPNService::Connect(Error* error, const char* reason) { if (IsConnected()) { Error::PopulateAndLog(FROM_HERE, error, Error::kAlreadyConnected, StringPrintf("VPN service %s already connected.", unique_name().c_str())); return; } if (IsConnecting()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInProgress, StringPrintf("VPN service %s already connecting.", unique_name().c_str())); return; } manager()->vpn_provider()->DisconnectAll(); Service::Connect(error, reason); driver_->Connect(this, error); } void VPNService::Disconnect(Error* error, const char* reason) { SLOG(this, 1) << "Disconnect from service " << unique_name(); Service::Disconnect(error, reason); driver_->Disconnect(); } string VPNService::GetStorageIdentifier() const { return storage_id_; } // static string VPNService::CreateStorageIdentifier(const KeyValueStore& args, Error* error) { string host = args.LookupString(kProviderHostProperty, ""); if (host.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kInvalidProperty, "Missing VPN host."); return ""; } string name = args.LookupString(kNameProperty, ""); if (name.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kNotSupported, "Missing VPN name."); return ""; } string id = StringPrintf("vpn_%s_%s", host.c_str(), name.c_str()); replace_if(id.begin(), id.end(), &Service::IllegalChar, '_'); return id; } string VPNService::GetDeviceRpcId(Error* error) const { error->Populate(Error::kNotSupported); return "/"; } bool VPNService::Load(StoreInterface* storage) { return Service::Load(storage) && driver_->Load(storage, GetStorageIdentifier()); } bool VPNService::Save(StoreInterface* storage) { return Service::Save(storage) && driver_->Save(storage, GetStorageIdentifier(), save_credentials()); } bool VPNService::Unload() { // The base method also disconnects the service. Service::Unload(); set_save_credentials(false); driver_->UnloadCredentials(); // Ask the VPN provider to remove us from its list. manager()->vpn_provider()->RemoveService(this); return true; } void VPNService::InitDriverPropertyStore() { driver_->InitPropertyStore(mutable_store()); } void VPNService::EnableAndRetainAutoConnect() { // The base EnableAndRetainAutoConnect method also sets auto_connect_ to true // which is not desirable for VPN services. RetainAutoConnect(); } void VPNService::SetConnection(const ConnectionRefPtr& connection) { // Construct the connection binder here rather than in the constructor because // there's really no reason to construct a binder if we never connect to this // service. It's safe to use an unretained callback to driver's method because // both the binder and the driver will be destroyed when this service is // destructed. if (!connection_binder_.get()) { connection_binder_.reset( new Connection::Binder(unique_name(), Bind(&VPNDriver::OnConnectionDisconnected, Unretained(driver_.get())))); } // Note that |connection_| is a reference-counted pointer and is always set // through this method. This means that the connection binder will not be // notified when the connection is destructed (because we will unbind it first // here when it's set to NULL, or because the binder will already be destroyed // by ~VPNService) -- it will be notified only if the connection disconnects // (e.g., because an underlying connection is destructed). connection_binder_->Attach(connection); Service::SetConnection(connection); } bool VPNService::IsAutoConnectable(const char** reason) const { if (!Service::IsAutoConnectable(reason)) { return false; } // Don't auto-connect VPN services that have never connected. This improves // the chances that the VPN service is connectable and avoids dialog popups. if (!has_ever_connected()) { *reason = kAutoConnNeverConnected; return false; } // Don't auto-connect a VPN service if another VPN service is already active. if (manager()->vpn_provider()->HasActiveService()) { *reason = kAutoConnVPNAlreadyActive; return false; } return true; } string VPNService::GetTethering(Error* error) const { ConnectionRefPtr conn = connection(); if (conn) conn = conn->GetCarrierConnection(); string tethering; if (conn) { tethering = conn->tethering(); if (!tethering.empty()) { return tethering; } // The underlying service may not have a Tethering property. This is // not strictly an error, so we don't print an error message. Populating // an error here just serves to propagate the lack of a property in // GetProperties(). error->Populate(Error::kNotSupported); } else { error->Populate(Error::kOperationFailed); } return ""; } bool VPNService::SetNameProperty(const string& name, Error* error) { if (name == friendly_name()) { return false; } LOG(INFO) << "Renaming service " << unique_name() << ": " << friendly_name() << " -> " << name; KeyValueStore* args = driver_->args(); args->SetString(kNameProperty, name); string new_storage_id = CreateStorageIdentifier(*args, error); if (new_storage_id.empty()) { return false; } string old_storage_id = storage_id_; DCHECK_NE(old_storage_id, new_storage_id); SetFriendlyName(name); // Update the storage identifier before invoking DeleteEntry to prevent it // from unloading this service. storage_id_ = new_storage_id; profile()->DeleteEntry(old_storage_id, nullptr); profile()->UpdateService(this); return true; } string VPNService::GetPhysicalTechnologyProperty(Error* error) { ConnectionRefPtr conn = connection(); if (conn) conn = conn->GetCarrierConnection(); if (!conn) { error->Populate(Error::kOperationFailed); return ""; } return Technology::NameFromIdentifier(conn->technology()); } } // namespace shill