261 lines
8.3 KiB
C++
261 lines
8.3 KiB
C++
//
|
|
// 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
|