blob: eb20e7d8a2e46566a50276808773ca7c366f9151 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/openvpn_driver.h"
#include <algorithm>
#include <base/file_path.h>
#include <base/file_util.h>
#include <base/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include "shill/dbus_adaptor.h"
#include "shill/error.h"
#include "shill/ipconfig.h"
#include "shill/logging.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_device_info.h"
#include "shill/mock_glib.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_nss.h"
#include "shill/mock_openvpn_management_server.h"
#include "shill/mock_process_killer.h"
#include "shill/mock_service.h"
#include "shill/mock_store.h"
#include "shill/mock_vpn.h"
#include "shill/mock_vpn_service.h"
#include "shill/nice_mock_control.h"
#include "shill/property_store_inspector.h"
#include "shill/rpc_task.h"
#include "shill/vpn.h"
#include "shill/vpn_service.h"
using base::WeakPtr;
using std::map;
using std::string;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::ElementsAreArray;
using testing::Field;
using testing::Ne;
using testing::NiceMock;
using testing::Return;
using testing::SetArgumentPointee;
using testing::StrictMock;
namespace shill {
class OpenVPNDriverTest : public testing::Test,
public RPCTaskDelegate {
public:
OpenVPNDriverTest()
: device_info_(&control_, &dispatcher_, &metrics_, &manager_),
manager_(&control_, &dispatcher_, &metrics_, &glib_),
driver_(new OpenVPNDriver(&control_, &dispatcher_, &metrics_, &manager_,
&device_info_, &glib_)),
service_(new MockVPNService(&control_, &dispatcher_, &metrics_,
&manager_, driver_)),
device_(new MockVPN(&control_, &dispatcher_, &metrics_, &manager_,
kInterfaceName, kInterfaceIndex)),
management_server_(new NiceMock<MockOpenVPNManagementServer>()) {
driver_->management_server_.reset(management_server_);
driver_->nss_ = &nss_;
driver_->process_killer_ = &process_killer_;
}
virtual ~OpenVPNDriverTest() {}
virtual void TearDown() {
driver_->default_service_callback_tag_ = 0;
driver_->child_watch_tag_ = 0;
driver_->pid_ = 0;
driver_->device_ = NULL;
driver_->service_ = NULL;
if (!lsb_release_file_.empty()) {
EXPECT_TRUE(file_util::Delete(lsb_release_file_, false));
lsb_release_file_.clear();
}
}
protected:
static const char kOption[];
static const char kProperty[];
static const char kValue[];
static const char kOption2[];
static const char kProperty2[];
static const char kValue2[];
static const char kGateway1[];
static const char kNetmask1[];
static const char kNetwork1[];
static const char kGateway2[];
static const char kNetmask2[];
static const char kNetwork2[];
static const char kInterfaceName[];
static const int kInterfaceIndex;
void SetArg(const string &arg, const string &value) {
driver_->args()->SetString(arg, value);
}
KeyValueStore *GetArgs() {
return driver_->args();
}
// Used to assert that a flag appears in the options.
void ExpectInFlags(const vector<string> &options, const string &flag,
const string &value);
void SetupLSBRelease();
// Inherited from RPCTaskDelegate.
virtual void GetLogin(string *user, string *password);
virtual void Notify(const string &reason, const map<string, string> &dict);
NiceMockControl control_;
NiceMock<MockDeviceInfo> device_info_;
EventDispatcher dispatcher_;
MockMetrics metrics_;
MockGLib glib_;
MockManager manager_;
OpenVPNDriver *driver_; // Owned by |service_|.
scoped_refptr<MockVPNService> service_;
scoped_refptr<MockVPN> device_;
MockNSS nss_;
MockProcessKiller process_killer_;
// Owned by |driver_|.
NiceMock<MockOpenVPNManagementServer> *management_server_;
FilePath lsb_release_file_;
};
const char OpenVPNDriverTest::kOption[] = "--openvpn-option";
const char OpenVPNDriverTest::kProperty[] = "OpenVPN.SomeProperty";
const char OpenVPNDriverTest::kValue[] = "some-property-value";
const char OpenVPNDriverTest::kOption2[] = "--openvpn-option2";
const char OpenVPNDriverTest::kProperty2[] = "OpenVPN.SomeProperty2";
const char OpenVPNDriverTest::kValue2[] = "some-property-value2";
const char OpenVPNDriverTest::kGateway1[] = "10.242.2.13";
const char OpenVPNDriverTest::kNetmask1[] = "255.255.255.255";
const char OpenVPNDriverTest::kNetwork1[] = "10.242.2.1";
const char OpenVPNDriverTest::kGateway2[] = "10.242.2.14";
const char OpenVPNDriverTest::kNetmask2[] = "255.255.0.0";
const char OpenVPNDriverTest::kNetwork2[] = "192.168.0.0";
const char OpenVPNDriverTest::kInterfaceName[] = "tun0";
const int OpenVPNDriverTest::kInterfaceIndex = 123;
void OpenVPNDriverTest::GetLogin(string */*user*/, string */*password*/) {}
void OpenVPNDriverTest::Notify(const string &/*reason*/,
const map<string, string> &/*dict*/) {}
void OpenVPNDriverTest::ExpectInFlags(const vector<string> &options,
const string &flag,
const string &value) {
vector<string>::const_iterator it =
std::find(options.begin(), options.end(), flag);
EXPECT_TRUE(it != options.end());
if (it != options.end())
return; // Don't crash below.
it++;
EXPECT_TRUE(it != options.end());
if (it != options.end())
return; // Don't crash below.
EXPECT_EQ(value, *it);
}
void OpenVPNDriverTest::SetupLSBRelease() {
static const char kLSBReleaseContents[] =
"\n"
"=\n"
"foo=\n"
"=bar\n"
"zoo==\n"
"CHROMEOS_RELEASE_BOARD=x86-alex\n"
"CHROMEOS_RELEASE_NAME=Chromium OS\n"
"CHROMEOS_RELEASE_VERSION=2202.0\n";
EXPECT_TRUE(file_util::CreateTemporaryFile(&lsb_release_file_));
EXPECT_EQ(arraysize(kLSBReleaseContents),
file_util::WriteFile(lsb_release_file_,
kLSBReleaseContents,
arraysize(kLSBReleaseContents)));
EXPECT_EQ(OpenVPNDriver::kLSBReleaseFile, driver_->lsb_release_file_.value());
driver_->lsb_release_file_ = lsb_release_file_;
}
TEST_F(OpenVPNDriverTest, Connect) {
EXPECT_CALL(*service_, SetState(Service::kStateConfiguring));
const string interface = kInterfaceName;
EXPECT_CALL(device_info_, CreateTunnelInterface(_))
.WillOnce(DoAll(SetArgumentPointee<0>(interface), Return(true)));
Error error;
driver_->Connect(service_, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_);
EXPECT_TRUE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, ConnectTunnelFailure) {
EXPECT_CALL(*service_, SetState(Service::kStateConfiguring));
EXPECT_CALL(device_info_, CreateTunnelInterface(_)).WillOnce(Return(false));
EXPECT_CALL(*service_, SetState(Service::kStateFailure));
Error error;
driver_->Connect(service_, &error);
EXPECT_EQ(Error::kInternalError, error.type());
EXPECT_TRUE(driver_->tunnel_interface_.empty());
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
}
namespace {
MATCHER_P(IsIPAddress, address, "") {
IPAddress ip_address(IPAddress::kFamilyIPv4);
EXPECT_TRUE(ip_address.SetAddressFromString(address));
return ip_address.Equals(arg);
}
} // namespace
TEST_F(OpenVPNDriverTest, Notify) {
map<string, string> config;
driver_->device_ = device_;
driver_->StartConnectTimeout();
EXPECT_CALL(*device_,
UpdateIPConfig(Field(&IPConfig::Properties::address, "")));
driver_->Notify("up", config);
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
// Tests that existing properties are reused if no new ones provided.
IPConfigRefPtr ipconfig(new IPConfig(&control_, device_->link_name()));
IPConfig::Properties props;
props.address = "1.2.3.4";
ipconfig->set_properties(props);
device_->set_ipconfig(ipconfig);
EXPECT_CALL(*device_,
UpdateIPConfig(Field(&IPConfig::Properties::address, "1.2.3.4")));
driver_->Notify("up", config);
}
TEST_F(OpenVPNDriverTest, NotifyFail) {
map<string, string> dict;
driver_->device_ = device_;
driver_->StartConnectTimeout();
EXPECT_CALL(*device_, OnDisconnected());
driver_->Notify("fail", dict);
EXPECT_TRUE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, GetRouteOptionEntry) {
OpenVPNDriver::RouteOptions routes;
EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "bar", &routes));
EXPECT_TRUE(routes.empty());
EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "foo", &routes));
EXPECT_TRUE(routes.empty());
EXPECT_EQ(NULL, OpenVPNDriver::GetRouteOptionEntry("foo", "fooZ", &routes));
EXPECT_TRUE(routes.empty());
IPConfig::Route *route =
OpenVPNDriver::GetRouteOptionEntry("foo", "foo12", &routes);
EXPECT_EQ(1, routes.size());
EXPECT_EQ(route, &routes[12]);
route = OpenVPNDriver::GetRouteOptionEntry("foo", "foo13", &routes);
EXPECT_EQ(2, routes.size());
EXPECT_EQ(route, &routes[13]);
}
TEST_F(OpenVPNDriverTest, ParseRouteOption) {
OpenVPNDriver::RouteOptions routes;
OpenVPNDriver::ParseRouteOption("foo", "bar", &routes);
EXPECT_TRUE(routes.empty());
OpenVPNDriver::ParseRouteOption("gateway_2", kGateway2, &routes);
OpenVPNDriver::ParseRouteOption("netmask_2", kNetmask2, &routes);
OpenVPNDriver::ParseRouteOption("network_2", kNetwork2, &routes);
EXPECT_EQ(1, routes.size());
OpenVPNDriver::ParseRouteOption("gateway_1", kGateway1, &routes);
OpenVPNDriver::ParseRouteOption("netmask_1", kNetmask1, &routes);
OpenVPNDriver::ParseRouteOption("network_1", kNetwork1, &routes);
EXPECT_EQ(2, routes.size());
EXPECT_EQ(kGateway1, routes[1].gateway);
EXPECT_EQ(kNetmask1, routes[1].netmask);
EXPECT_EQ(kNetwork1, routes[1].host);
EXPECT_EQ(kGateway2, routes[2].gateway);
EXPECT_EQ(kNetmask2, routes[2].netmask);
EXPECT_EQ(kNetwork2, routes[2].host);
}
TEST_F(OpenVPNDriverTest, SetRoutes) {
OpenVPNDriver::RouteOptions routes;
routes[1].gateway = "1.2.3.4";
routes[1].host= "1.2.3.4";
routes[2].host = "2.3.4.5";
routes[2].netmask = "255.0.0.0";
routes[3].netmask = "255.0.0.0";
routes[3].gateway = "1.2.3.5";
routes[5].host = kNetwork2;
routes[5].netmask = kNetmask2;
routes[5].gateway = kGateway2;
routes[4].host = kNetwork1;
routes[4].netmask = kNetmask1;
routes[4].gateway = kGateway1;
IPConfig::Properties props;
OpenVPNDriver::SetRoutes(routes, &props);
ASSERT_EQ(2, props.routes.size());
EXPECT_EQ(kGateway1, props.routes[0].gateway);
EXPECT_EQ(kNetmask1, props.routes[0].netmask);
EXPECT_EQ(kNetwork1, props.routes[0].host);
EXPECT_EQ(kGateway2, props.routes[1].gateway);
EXPECT_EQ(kNetmask2, props.routes[1].netmask);
EXPECT_EQ(kNetwork2, props.routes[1].host);
// Tests that the routes are not reset if no new routes are supplied.
OpenVPNDriver::SetRoutes(OpenVPNDriver::RouteOptions(), &props);
EXPECT_EQ(2, props.routes.size());
}
TEST_F(OpenVPNDriverTest, ParseForeignOption) {
vector<string> domain_search;
vector<string> dns_servers;
IPConfig::Properties props;
OpenVPNDriver::ParseForeignOption("", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DOMAIN", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DOMAIN zzz.com foo", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-Option DOmAIN xyz.com", &domain_search, &dns_servers);
ASSERT_EQ(1, domain_search.size());
EXPECT_EQ("xyz.com", domain_search[0]);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DnS 1.2.3.4", &domain_search, &dns_servers);
ASSERT_EQ(1, dns_servers.size());
EXPECT_EQ("1.2.3.4", dns_servers[0]);
}
TEST_F(OpenVPNDriverTest, ParseForeignOptions) {
// This also tests that std::map is a sorted container.
map<int, string> options;
options[5] = "dhcp-option DOMAIN five.com";
options[2] = "dhcp-option DOMAIN two.com";
options[8] = "dhcp-option DOMAIN eight.com";
options[7] = "dhcp-option DOMAIN seven.com";
options[4] = "dhcp-option DOMAIN four.com";
options[10] = "dhcp-option dns 1.2.3.4";
IPConfig::Properties props;
OpenVPNDriver::ParseForeignOptions(options, &props);
ASSERT_EQ(5, props.domain_search.size());
EXPECT_EQ("two.com", props.domain_search[0]);
EXPECT_EQ("four.com", props.domain_search[1]);
EXPECT_EQ("five.com", props.domain_search[2]);
EXPECT_EQ("seven.com", props.domain_search[3]);
EXPECT_EQ("eight.com", props.domain_search[4]);
ASSERT_EQ(1, props.dns_servers.size());
EXPECT_EQ("1.2.3.4", props.dns_servers[0]);
// Test that the DNS properties are not updated if no new DNS properties are
// supplied.
OpenVPNDriver::ParseForeignOptions(map<int, string>(), &props);
EXPECT_EQ(5, props.domain_search.size());
ASSERT_EQ(1, props.dns_servers.size());
}
TEST_F(OpenVPNDriverTest, ParseIPConfiguration) {
map<string, string> config;
IPConfig::Properties props;
OpenVPNDriver::ParseIPConfiguration(config, &props);
EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family);
EXPECT_EQ(32, props.subnet_prefix);
props.subnet_prefix = 18;
OpenVPNDriver::ParseIPConfiguration(config, &props);
EXPECT_EQ(18, props.subnet_prefix);
config["ifconfig_loCal"] = "4.5.6.7";
config["ifconfiG_broadcast"] = "1.2.255.255";
config["ifconFig_netmAsk"] = "255.255.255.0";
config["ifconfig_remotE"] = "33.44.55.66";
config["route_vpN_gateway"] = "192.168.1.1";
config["trusted_ip"] = "99.88.77.66";
config["tun_mtu"] = "1000";
config["foreign_option_2"] = "dhcp-option DNS 4.4.4.4";
config["foreign_option_1"] = "dhcp-option DNS 1.1.1.1";
config["foreign_option_3"] = "dhcp-option DNS 2.2.2.2";
config["route_network_2"] = kNetwork2;
config["route_network_1"] = kNetwork1;
config["route_netmask_2"] = kNetmask2;
config["route_netmask_1"] = kNetmask1;
config["route_gateway_2"] = kGateway2;
config["route_gateway_1"] = kGateway1;
config["foo"] = "bar";
OpenVPNDriver::ParseIPConfiguration(config, &props);
EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family);
EXPECT_EQ("4.5.6.7", props.address);
EXPECT_EQ("1.2.255.255", props.broadcast_address);
EXPECT_EQ(24, props.subnet_prefix);
EXPECT_EQ("33.44.55.66", props.peer_address);
EXPECT_EQ("192.168.1.1", props.gateway);
EXPECT_EQ("99.88.77.66", props.trusted_ip);
EXPECT_EQ(1000, props.mtu);
ASSERT_EQ(3, props.dns_servers.size());
EXPECT_EQ("1.1.1.1", props.dns_servers[0]);
EXPECT_EQ("4.4.4.4", props.dns_servers[1]);
EXPECT_EQ("2.2.2.2", props.dns_servers[2]);
ASSERT_EQ(2, props.routes.size());
EXPECT_EQ(kGateway1, props.routes[0].gateway);
EXPECT_EQ(kNetmask1, props.routes[0].netmask);
EXPECT_EQ(kNetwork1, props.routes[0].host);
EXPECT_EQ(kGateway2, props.routes[1].gateway);
EXPECT_EQ(kNetmask2, props.routes[1].netmask);
EXPECT_EQ(kNetwork2, props.routes[1].host);
}
TEST_F(OpenVPNDriverTest, InitOptionsNoHost) {
Error error;
vector<string> options;
driver_->InitOptions(&options, &error);
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_TRUE(options.empty());
}
TEST_F(OpenVPNDriverTest, InitOptions) {
static const char kHost[] = "192.168.2.254";
static const char kTLSAuthContents[] = "SOME-RANDOM-CONTENTS\n";
static const char kCaCertNSS[] = "{1234}";
static const char kID[] = "TestPKCS11ID";
FilePath empty_cert;
SetArg(flimflam::kProviderHostProperty, kHost);
SetArg(flimflam::kOpenVPNTLSAuthContentsProperty, kTLSAuthContents);
SetArg(flimflam::kOpenVPNCaCertNSSProperty, kCaCertNSS);
SetArg(flimflam::kOpenVPNClientCertIdProperty, kID);
driver_->rpc_task_.reset(new RPCTask(&control_, this));
driver_->tunnel_interface_ = kInterfaceName;
EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true));
EXPECT_CALL(nss_, GetPEMCertfile(kCaCertNSS, _)).WillOnce(Return(empty_cert));
ServiceRefPtr null_service;
EXPECT_CALL(manager_, GetDefaultService()).WillOnce(Return(null_service));
Error error;
vector<string> options;
driver_->InitOptions(&options, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ("--client", options[0]);
ExpectInFlags(options, "--remote", kHost);
ExpectInFlags(options, "CONNMAN_PATH", RPCTaskMockAdaptor::kRpcId);
ExpectInFlags(options, "--dev", kInterfaceName);
ExpectInFlags(options, "--group", "openvpn");
EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_);
ASSERT_FALSE(driver_->tls_auth_file_.empty());
ExpectInFlags(options, "--tls-auth", driver_->tls_auth_file_.value());
string contents;
EXPECT_TRUE(
file_util::ReadFileToString(driver_->tls_auth_file_, &contents));
EXPECT_EQ(kTLSAuthContents, contents);
ExpectInFlags(options, "--pkcs11-id", kID);
EXPECT_TRUE(std::find(options.begin(), options.end(), "--syslog") !=
options.end());
}
TEST_F(OpenVPNDriverTest, InitNSSOptions) {
static const char kHost[] = "192.168.2.254";
static const char kCaCertNSS[] = "{1234}";
static const char kNSSCertfile[] = "/tmp/nss-cert";
FilePath empty_cert;
FilePath nss_cert(kNSSCertfile);
SetArg(flimflam::kProviderHostProperty, kHost);
SetArg(flimflam::kOpenVPNCaCertNSSProperty, kCaCertNSS);
EXPECT_CALL(nss_,
GetPEMCertfile(kCaCertNSS,
ElementsAreArray(kHost, arraysize(kHost) - 1)))
.WillOnce(Return(empty_cert))
.WillOnce(Return(nss_cert));
Error error;
vector<string> options;
EXPECT_TRUE(driver_->InitNSSOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_TRUE(options.empty());
EXPECT_TRUE(driver_->InitNSSOptions(&options, &error));
ExpectInFlags(options, "--ca", kNSSCertfile);
EXPECT_TRUE(error.IsSuccess());
SetArg(flimflam::kOpenVPNCaCertProperty, "foo");
options.clear();
EXPECT_FALSE(driver_->InitNSSOptions(&options, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_EQ("Can't specify both CACert and CACertNSS.", error.message());
}
TEST_F(OpenVPNDriverTest, InitPKCS11Options) {
vector<string> options;
driver_->InitPKCS11Options(&options);
EXPECT_TRUE(options.empty());
static const char kID[] = "TestPKCS11ID";
SetArg(flimflam::kOpenVPNClientCertIdProperty, kID);
driver_->InitPKCS11Options(&options);
ExpectInFlags(options, "--pkcs11-id", kID);
ExpectInFlags(options, "--pkcs11-providers", "libchaps.so");
static const char kProvider[] = "libpkcs11.so";
SetArg(flimflam::kOpenVPNProviderProperty, kProvider);
options.clear();
driver_->InitPKCS11Options(&options);
ExpectInFlags(options, "--pkcs11-id", kID);
ExpectInFlags(options, "--pkcs11-providers", kProvider);
}
TEST_F(OpenVPNDriverTest, InitManagementChannelOptions) {
vector<string> options;
Error error;
EXPECT_CALL(*management_server_,
Start(&dispatcher_, &driver_->sockets_, &options))
.Times(4)
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
// Management server fails to start.
EXPECT_FALSE(driver_->InitManagementChannelOptions(&options, &error));
EXPECT_EQ(Error::kInternalError, error.type());
EXPECT_EQ("Unable to setup management channel.", error.message());
// Start with a connected default service.
scoped_refptr<MockService> mock_service(
new MockService(&control_, &dispatcher_, &metrics_, &manager_));
EXPECT_CALL(manager_, GetDefaultService()).WillOnce(Return(mock_service));
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(true));
EXPECT_CALL(*management_server_, ReleaseHold());
error.Reset();
EXPECT_TRUE(driver_->InitManagementChannelOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
// Start with a disconnected default service.
EXPECT_CALL(manager_, GetDefaultService()).WillOnce(Return(mock_service));
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(false));
EXPECT_CALL(*management_server_, ReleaseHold()).Times(0);
EXPECT_TRUE(driver_->InitManagementChannelOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
// Start with no default service.
ServiceRefPtr null_service;
EXPECT_CALL(manager_, GetDefaultService()).WillOnce(Return(null_service));
EXPECT_CALL(*management_server_, ReleaseHold()).Times(0);
EXPECT_TRUE(driver_->InitManagementChannelOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(OpenVPNDriverTest, InitLoggingOptions) {
vector<string> options;
bool vpn_logging = SLOG_IS_ON(VPN, 0);
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
driver_->InitLoggingOptions(&options);
ASSERT_EQ(1, options.size());
EXPECT_EQ("--syslog", options[0]);
ScopeLogger::GetInstance()->EnableScopesByName("+vpn");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "--verb", "3");
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
SetArg("OpenVPN.Verb", "2");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "--verb", "2");
ScopeLogger::GetInstance()->EnableScopesByName("+vpn");
SetArg("OpenVPN.Verb", "1");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "--verb", "1");
if (!vpn_logging) {
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
}
}
TEST_F(OpenVPNDriverTest, AppendValueOption) {
vector<string> options;
EXPECT_FALSE(
driver_->AppendValueOption("OpenVPN.UnknownProperty", kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, "");
EXPECT_FALSE(driver_->AppendValueOption(kProperty, kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, kValue);
SetArg(kProperty2, kValue2);
EXPECT_TRUE(driver_->AppendValueOption(kProperty, kOption, &options));
EXPECT_TRUE(driver_->AppendValueOption(kProperty2, kOption2, &options));
EXPECT_EQ(4, options.size());
EXPECT_EQ(kOption, options[0]);
EXPECT_EQ(kValue, options[1]);
EXPECT_EQ(kOption2, options[2]);
EXPECT_EQ(kValue2, options[3]);
}
TEST_F(OpenVPNDriverTest, AppendFlag) {
vector<string> options;
EXPECT_FALSE(
driver_->AppendFlag("OpenVPN.UnknownProperty", kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, "");
SetArg(kProperty2, kValue2);
EXPECT_TRUE(driver_->AppendFlag(kProperty, kOption, &options));
EXPECT_TRUE(driver_->AppendFlag(kProperty2, kOption2, &options));
EXPECT_EQ(2, options.size());
EXPECT_EQ(kOption, options[0]);
EXPECT_EQ(kOption2, options[1]);
}
TEST_F(OpenVPNDriverTest, ClaimInterface) {
driver_->tunnel_interface_ = kInterfaceName;
EXPECT_FALSE(driver_->ClaimInterface(string(kInterfaceName) + "XXX",
kInterfaceIndex));
EXPECT_FALSE(driver_->device_);
static const char kHost[] = "192.168.2.254";
SetArg(flimflam::kProviderHostProperty, kHost);
EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true));
ServiceRefPtr null_service;
EXPECT_CALL(manager_, GetDefaultService()).WillOnce(Return(null_service));
EXPECT_CALL(glib_, SpawnAsyncWithPipesCWD(_, _, _, _, _, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(glib_, ChildWatchAdd(_, _, _)).WillOnce(Return(1));
const int kServiceCallbackTag = 1;
EXPECT_EQ(0, driver_->default_service_callback_tag_);
EXPECT_CALL(manager_, RegisterDefaultServiceCallback(_))
.WillOnce(Return(kServiceCallbackTag));
EXPECT_TRUE(driver_->ClaimInterface(kInterfaceName, kInterfaceIndex));
ASSERT_TRUE(driver_->device_);
EXPECT_EQ(kInterfaceIndex, driver_->device_->interface_index());
EXPECT_EQ(kServiceCallbackTag, driver_->default_service_callback_tag_);
}
TEST_F(OpenVPNDriverTest, Cleanup) {
driver_->Cleanup(Service::kStateIdle); // Ensure no crash.
const unsigned int kChildTag = 123;
const int kPID = 123456;
const int kServiceCallbackTag = 5;
driver_->default_service_callback_tag_ = kServiceCallbackTag;
driver_->child_watch_tag_ = kChildTag;
driver_->pid_ = kPID;
driver_->rpc_task_.reset(new RPCTask(&control_, this));
driver_->tunnel_interface_ = kInterfaceName;
driver_->device_ = device_;
driver_->service_ = service_;
driver_->StartConnectTimeout();
FilePath tls_auth_file;
EXPECT_TRUE(file_util::CreateTemporaryFile(&tls_auth_file));
EXPECT_FALSE(tls_auth_file.empty());
EXPECT_TRUE(file_util::PathExists(tls_auth_file));
driver_->tls_auth_file_ = tls_auth_file;
// Stop will be called twice -- once by Cleanup and once by the destructor.
EXPECT_CALL(*management_server_, Stop()).Times(2);
EXPECT_CALL(glib_, SourceRemove(kChildTag));
EXPECT_CALL(manager_, DeregisterDefaultServiceCallback(kServiceCallbackTag));
EXPECT_CALL(process_killer_, Kill(kPID, _));
EXPECT_CALL(device_info_, DeleteInterface(_)).Times(0);
EXPECT_CALL(*device_, OnDisconnected());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(*service_, SetState(Service::kStateFailure));
driver_->Cleanup(Service::kStateFailure);
EXPECT_EQ(0, driver_->child_watch_tag_);
EXPECT_EQ(0, driver_->default_service_callback_tag_);
EXPECT_EQ(0, driver_->pid_);
EXPECT_FALSE(driver_->rpc_task_.get());
EXPECT_TRUE(driver_->tunnel_interface_.empty());
EXPECT_FALSE(driver_->device_);
EXPECT_FALSE(driver_->service_);
EXPECT_FALSE(file_util::PathExists(tls_auth_file));
EXPECT_TRUE(driver_->tls_auth_file_.empty());
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
}
namespace {
MATCHER(CheckEnv, "") {
if (!arg || !arg[0] || !arg[1] || arg[2]) {
return false;
}
return (string(arg[0]) == "IV_PLAT=Chromium OS" &&
string(arg[1]) == "IV_PLAT_REL=2202.0");
}
} // namespace
TEST_F(OpenVPNDriverTest, SpawnOpenVPN) {
SetupLSBRelease();
EXPECT_FALSE(driver_->SpawnOpenVPN());
static const char kHost[] = "192.168.2.254";
SetArg(flimflam::kProviderHostProperty, kHost);
driver_->tunnel_interface_ = "tun0";
driver_->rpc_task_.reset(new RPCTask(&control_, this));
EXPECT_CALL(*management_server_, Start(_, _, _))
.Times(2)
.WillRepeatedly(Return(true));
ServiceRefPtr null_service;
EXPECT_CALL(manager_, GetDefaultService())
.Times(2)
.WillRepeatedly(Return(null_service));
const int kPID = 234678;
EXPECT_CALL(glib_,
SpawnAsyncWithPipesCWD(_, CheckEnv(), _, _, _, _, _, _, _, _))
.WillOnce(Return(false))
.WillOnce(DoAll(SetArgumentPointee<5>(kPID), Return(true)));
const int kTag = 6;
EXPECT_CALL(glib_, ChildWatchAdd(kPID, &driver_->OnOpenVPNDied, driver_))
.WillOnce(Return(kTag));
EXPECT_FALSE(driver_->SpawnOpenVPN());
EXPECT_TRUE(driver_->SpawnOpenVPN());
EXPECT_EQ(kPID, driver_->pid_);
EXPECT_EQ(kTag, driver_->child_watch_tag_);
}
TEST_F(OpenVPNDriverTest, OnOpenVPNDied) {
const int kPID = 99999;
driver_->device_ = device_;
driver_->child_watch_tag_ = 333;
driver_->pid_ = kPID;
EXPECT_CALL(*device_, OnDisconnected());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(process_killer_, Kill(_, _)).Times(0);
EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
OpenVPNDriver::OnOpenVPNDied(kPID, 2, driver_);
EXPECT_EQ(0, driver_->child_watch_tag_);
EXPECT_EQ(0, driver_->pid_);
}
TEST_F(OpenVPNDriverTest, Disconnect) {
driver_->device_ = device_;
driver_->service_ = service_;
EXPECT_CALL(*device_, OnDisconnected());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
EXPECT_CALL(*service_, SetState(Service::kStateIdle));
driver_->Disconnect();
EXPECT_FALSE(driver_->device_);
EXPECT_FALSE(driver_->service_);
}
TEST_F(OpenVPNDriverTest, OnConnectionDisconnected) {
driver_->service_ = service_;
EXPECT_CALL(*service_, SetState(Service::kStateFailure));
driver_->OnConnectionDisconnected();
EXPECT_FALSE(driver_->service_);
}
TEST_F(OpenVPNDriverTest, OnReconnecting) {
driver_->OnReconnecting(); // Expect no crash.
driver_->device_ = device_;
driver_->service_ = service_;
EXPECT_CALL(*device_, OnDisconnected());
EXPECT_CALL(*service_, SetState(Service::kStateAssociating));
driver_->OnReconnecting();
EXPECT_TRUE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, VerifyPaths) {
// Ensure that the various path constants that the OpenVPN driver uses
// actually exists in the build image. Due to build dependencies, they should
// already exist by the time we run unit tests.
// The OpenVPNDriver path constants are absolute. FilePath::Append asserts
// that its argument is not an absolute path, so we need to strip the leading
// separators. There's nothing built into FilePath to do so.
static const char *kPaths[] = {
OpenVPNDriver::kOpenVPNPath,
OpenVPNDriver::kOpenVPNScript,
};
for (size_t i = 0; i < arraysize(kPaths); i++) {
string path(kPaths[i]);
TrimString(path, FilePath::kSeparators, &path);
EXPECT_TRUE(file_util::PathExists(FilePath(SYSROOT).Append(path)))
<< kPaths[i];
}
}
TEST_F(OpenVPNDriverTest, InitPropertyStore) {
// Sanity test property store initialization.
PropertyStore store;
driver_->InitPropertyStore(&store);
const string kUser = "joe";
Error error;
EXPECT_TRUE(
store.SetStringProperty(flimflam::kOpenVPNUserProperty, kUser, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kUser, GetArgs()->LookupString(flimflam::kOpenVPNUserProperty, ""));
}
TEST_F(OpenVPNDriverTest, GetProvider) {
PropertyStore store;
driver_->InitPropertyStore(&store);
PropertyStoreInspector inspector(&store);
{
KeyValueStore props;
EXPECT_TRUE(
inspector.GetKeyValueStoreProperty(
flimflam::kProviderProperty, &props));
EXPECT_TRUE(props.LookupBool(flimflam::kPassphraseRequiredProperty, false));
}
{
KeyValueStore props;
SetArg(flimflam::kOpenVPNPasswordProperty, "random-password");
EXPECT_TRUE(
inspector.GetKeyValueStoreProperty(
flimflam::kProviderProperty, &props));
EXPECT_FALSE(props.LookupBool(flimflam::kPassphraseRequiredProperty, true));
EXPECT_FALSE(props.ContainsString(flimflam::kOpenVPNPasswordProperty));
}
}
TEST_F(OpenVPNDriverTest, ParseLSBRelease) {
SetupLSBRelease();
map<string, string> lsb_release;
EXPECT_TRUE(driver_->ParseLSBRelease(&lsb_release));
EXPECT_TRUE(ContainsKey(lsb_release, "foo") && lsb_release["foo"] == "");
EXPECT_EQ("=", lsb_release["zoo"]);
EXPECT_EQ("Chromium OS", lsb_release[OpenVPNDriver::kChromeOSReleaseName]);
EXPECT_EQ("2202.0", lsb_release[OpenVPNDriver::kChromeOSReleaseVersion]);
driver_->lsb_release_file_ = FilePath("/non/existent/file");
EXPECT_FALSE(driver_->ParseLSBRelease(NULL));
}
TEST_F(OpenVPNDriverTest, InitEnvironment) {
vector<string> env;
SetupLSBRelease();
driver_->InitEnvironment(&env);
ASSERT_EQ(2, env.size());
EXPECT_EQ("IV_PLAT=Chromium OS", env[0]);
EXPECT_EQ("IV_PLAT_REL=2202.0", env[1]);
env.clear();
EXPECT_EQ(0, file_util::WriteFile(lsb_release_file_, "", 0));
driver_->InitEnvironment(&env);
EXPECT_EQ(0, env.size());
}
TEST_F(OpenVPNDriverTest, DeleteInterface) {
scoped_ptr<MockDeviceInfo> device_info(
new MockDeviceInfo(&control_, &dispatcher_, &metrics_, &manager_));
EXPECT_CALL(*device_info, DeleteInterface(kInterfaceIndex))
.WillOnce(Return(true));
WeakPtr<DeviceInfo> weak = device_info->AsWeakPtr();
EXPECT_TRUE(weak);
OpenVPNDriver::DeleteInterface(weak, kInterfaceIndex);
device_info.reset();
EXPECT_FALSE(weak);
// Expect no crash.
OpenVPNDriver::DeleteInterface(weak, kInterfaceIndex);
}
TEST_F(OpenVPNDriverTest, OnDefaultServiceChanged) {
driver_->service_ = service_;
ServiceRefPtr null_service;
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(null_service);
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(service_);
scoped_refptr<MockService> mock_service(
new MockService(&control_, &dispatcher_, &metrics_, &manager_));
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(false));
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(mock_service);
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(true));
EXPECT_CALL(*management_server_, ReleaseHold());
driver_->OnDefaultServiceChanged(mock_service);
}
} // namespace shill