Home | History | Annotate | Download | only in vpn
      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;
     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 
     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 
     72 VPNService::~VPNService() {}
     73 
     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 
     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 
     98 string VPNService::GetStorageIdentifier() const {
     99   return storage_id_;
    100 }
    101 
    102 // static
    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 
    122 string VPNService::GetDeviceRpcId(Error* error) const {
    123   error->Populate(Error::kNotSupported);
    124   return "/";
    125 }
    126 
    127 bool VPNService::Load(StoreInterface* storage) {
    128   return Service::Load(storage) &&
    129       driver_->Load(storage, GetStorageIdentifier());
    130 }
    131 
    132 bool VPNService::Save(StoreInterface* storage) {
    133   return Service::Save(storage) &&
    134       driver_->Save(storage, GetStorageIdentifier(), save_credentials());
    135 }
    136 
    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 
    150 void VPNService::InitDriverPropertyStore() {
    151   driver_->InitPropertyStore(mutable_store());
    152 }
    153 
    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 
    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 
    182 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 
    200 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 
    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 
    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