blob: afb66d8fff087c42418f9efbb94b34120d54bf32 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "chrome/browser/chromeos/smb_client/discovery/netbios_host_locator.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/test_mock_time_task_runner.h"
#include "chrome/browser/chromeos/smb_client/discovery/fake_netbios_client.h"
#include "chrome/browser/chromeos/smb_client/smb_constants.h"
#include "chromeos/dbus/fake_smb_provider_client.h"
#include "net/base/ip_endpoint.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace smb_client {
namespace {
// Helper method to create a NetworkInterface for testing.
net::NetworkInterface CreateNetworkInterface(
const net::IPAddress& address,
uint32_t prefix_length,
net::NetworkChangeNotifier::ConnectionType type) {
net::NetworkInterface interface;
interface.address = address;
interface.prefix_length = prefix_length;
interface.type = type;
return interface;
}
net::NetworkInterface CreateValidInterface() {
return CreateNetworkInterface(net::IPAddress::IPv4Localhost(), 0,
net::NetworkChangeNotifier::CONNECTION_WIFI);
}
net::NetworkInterface GenerateInvalidInterface() {
return CreateNetworkInterface(net::IPAddress::IPv4Localhost(), 0,
net::NetworkChangeNotifier::CONNECTION_3G);
}
net::NetworkInterfaceList GenerateInvalidInterfaceList(size_t num) {
return net::NetworkInterfaceList(num, GenerateInvalidInterface());
}
void ExpectNoResults(bool success, const HostMap& hosts) {
EXPECT_TRUE(success);
EXPECT_TRUE(hosts.empty());
}
void ExpectResultsEqual(const HostMap& expected,
bool success,
const HostMap& actual) {
EXPECT_TRUE(success);
EXPECT_EQ(expected, actual);
}
void ExpectFailure(bool success, const HostMap& hosts) {
EXPECT_FALSE(success);
EXPECT_TRUE(hosts.empty());
}
} // namespace
class NetBiosHostLocatorTest : public testing::Test {
public:
NetBiosHostLocatorTest() {
// Fake SmbProviderClient that simulates the parsing of NetBios response
// packets.
fake_provider_client_ = std::make_unique<FakeSmbProviderClient>();
// Bound function to get interfaces. For testing we generate a valid
// interface for each NetBiosClient in the test so that the correct number
// of NetBiosClients are created.
get_interfaces_ =
base::BindRepeating(&NetBiosHostLocatorTest::GenerateValidInterfaces,
base::Unretained(this));
// NetBiosClientFactory. Creates a FakeNetBiosClient using the fake response
// data in |clients_data_|, if any exists.
client_factory_ = base::BindRepeating(
&NetBiosHostLocatorTest::NetBiosClientFactory, base::Unretained(this));
// Set up taskrunner and timer.
task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
timer_ =
std::make_unique<base::OneShotTimer>(task_runner_->GetMockTickClock());
timer_->SetTaskRunner(task_runner_);
// Callback used for setting |has_returned_|.
set_true_on_returned_callback_ = base::BindOnce(
&NetBiosHostLocatorTest::SetTrueOnReturned, base::Unretained(this));
}
~NetBiosHostLocatorTest() override = default;
protected:
using Packet = std::vector<uint8_t>;
using Hostnames = std::vector<std::string>;
// Adds the data for one NetBiosClient. The NetBiosClient will return an
// <IPEndpoint, packet>, for each response and this packet can be parsed by
// FakeSmbProviderClient returning the hostnames. A |packet_id| is used to
// correlate the packet that will be returned by the FakeNetBiosClient and
// the Hostnames that the FakeSmbProviderClient should return.
void AddNetBiosClient(const std::map<net::IPEndPoint, Hostnames>& responses) {
std::map<net::IPEndPoint, Packet> client_data;
for (const auto& kv : responses) {
client_data[kv.first] = Packet{packet_id_};
fake_provider_client_->AddNetBiosPacketParsingForTesting(packet_id_,
kv.second);
++packet_id_;
}
clients_data_.push_back(std::move(client_data));
}
bool has_returned_ = false;
FindHostsCallback set_true_on_returned_callback_;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
std::unique_ptr<base::OneShotTimer> timer_;
NetBiosHostLocator::GetInterfacesFunction get_interfaces_;
NetBiosHostLocator::NetBiosClientFactory client_factory_;
std::unique_ptr<FakeSmbProviderClient> fake_provider_client_;
std::unique_ptr<NetBiosHostLocator> host_locator_;
private:
// Creates valid interfaces such that there is one valid interface for each
// NetBios Client that needs to be created.
net::NetworkInterfaceList GenerateValidInterfaces() {
return net::NetworkInterfaceList(clients_data_.size(),
CreateValidInterface());
}
// Factory Function for FakeNetBiosClient. Returns a pointer to a
// FakeNetBiosClient preloaded with corresponding data from |clients_data|.
std::unique_ptr<NetBiosClientInterface> NetBiosClientFactory() {
if (current_client < clients_data_.size()) {
return std::make_unique<FakeNetBiosClient>(
clients_data_[current_client++]);
}
return std::make_unique<FakeNetBiosClient>();
}
// Callback for FindHosts that sets |has_returned_| to true when called.
void SetTrueOnReturned(bool success, const HostMap& results) {
DCHECK(!has_returned_);
has_returned_ = true;
}
uint8_t packet_id_ = 0;
uint8_t current_client = 0;
// Each entry in the vector represents on Netbios Client that can be created.
// Each entry in the map represents an IP, packet pair that should be returned
// by that NetBiosClient.
std::vector<std::map<net::IPEndPoint, Packet>> clients_data_;
DISALLOW_COPY_AND_ASSIGN(NetBiosHostLocatorTest);
};
// Calculate broadcast address correctly calculates the broadcast address
// of a NetworkInterface.
TEST_F(NetBiosHostLocatorTest, CalculateBroadcastAddress) {
const net::NetworkInterface interface = CreateNetworkInterface(
net::IPAddress(192, 168, 50, 152), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_EQ(net::IPAddress(192, 168, 50, 255),
CalculateBroadcastAddress(interface));
}
// ShouldUseInterface returns true for Wifi and Ethernet interfaces but false
// for other types of interfaces.
TEST_F(NetBiosHostLocatorTest, ShouldUseWifiAndEthernetInterfaces) {
const net::NetworkInterface interface_wifi = CreateNetworkInterface(
net::IPAddress::IPv4Localhost(), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_WIFI);
const net::NetworkInterface interface_ethernet = CreateNetworkInterface(
net::IPAddress::IPv4Localhost(), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_WIFI);
const net::NetworkInterface interface_bluetooth = CreateNetworkInterface(
net::IPAddress::IPv4Localhost(), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
EXPECT_TRUE(ShouldUseInterface(interface_wifi));
EXPECT_TRUE(ShouldUseInterface(interface_ethernet));
EXPECT_FALSE(ShouldUseInterface(interface_bluetooth));
}
// ShouldUseInterface returns true for IPv4 interfaces but false for IPv6
// interfaces.
TEST_F(NetBiosHostLocatorTest, OnlyProcessIPv4Interfaces) {
const net::NetworkInterface interface_ipv4 = CreateNetworkInterface(
net::IPAddress::IPv4Localhost(), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_WIFI);
const net::NetworkInterface interface_ipv6 = CreateNetworkInterface(
net::IPAddress::IPv6Localhost(), 24 /* prefix_length */,
net::NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_TRUE(ShouldUseInterface(interface_ipv4));
EXPECT_FALSE(ShouldUseInterface(interface_ipv6));
}
// One interface that receives no responses properly returns no results.
TEST_F(NetBiosHostLocatorTest, OneInterfaceNoResults) {
// Add the entry for a NetBios Client that returns no packets.
AddNetBiosClient(std::map<net::IPEndPoint, Hostnames>());
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(base::BindOnce(&ExpectNoResults));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
// Two interfaces that receive no responses properly return no results.
TEST_F(NetBiosHostLocatorTest, MultipleInterfacesNoResults) {
// Create two NetBiosClients that don't return any packets.
AddNetBiosClient(std::map<net::IPEndPoint, Hostnames>());
AddNetBiosClient(std::map<net::IPEndPoint, Hostnames>());
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(base::BindOnce(&ExpectNoResults));
// Fast forward timer so that callback fires.
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
// One interface that recieves responses from two different ip addresses
// returns the correct results.
TEST_F(NetBiosHostLocatorTest, OneInterfaceWithResults) {
// Build data for a NetBiosClient
const net::IPEndPoint source_ip_1(net::IPAddress(1, 2, 3, 4), 137);
const Hostnames hostnames_1 = {"hostname1", "HOSTNAME_2"};
const net::IPEndPoint source_ip_2(net::IPAddress(2, 4, 6, 8), 137);
const Hostnames hostnames_2 = {"host.name.3"};
std::map<net::IPEndPoint, Hostnames> netbios_client_1;
netbios_client_1[source_ip_1] = hostnames_1;
netbios_client_1[source_ip_2] = hostnames_2;
// Build the map of expected results.
HostMap expected_results;
expected_results[hostnames_1[0]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_1[1]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_2[0]] = source_ip_2.ToStringWithoutPort();
// Add the entry for a NetBios Client that returns packets.
AddNetBiosClient(netbios_client_1);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(
base::BindOnce(&ExpectResultsEqual, expected_results));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
// Two interfaces that each receive responses from multiple ip addresses
// correctly returns results.
TEST_F(NetBiosHostLocatorTest, MultipleInterfacesWithResults) {
// Build data for the first NetBiosClient
const net::IPEndPoint source_ip_1(net::IPAddress(1, 2, 3, 4), 137);
const Hostnames hostnames_1 = {"hostname1", "HOSTNAME_2"};
const net::IPEndPoint source_ip_2(net::IPAddress(2, 4, 6, 8), 137);
const Hostnames hostnames_2 = {"host.name.3"};
std::map<net::IPEndPoint, Hostnames> netbios_client_1;
netbios_client_1[source_ip_1] = hostnames_1;
netbios_client_1[source_ip_2] = hostnames_2;
// Build data for the second NetBiosClient
const net::IPEndPoint source_ip_3(net::IPAddress(1, 3, 5, 9), 137);
const Hostnames hostnames_3 = {"host name 4"};
const net::IPEndPoint source_ip_4(net::IPAddress(2, 4, 8, 16), 137);
const Hostnames hostnames_4 = {"hOsTnAmE-5"};
std::map<net::IPEndPoint, Hostnames> netbios_client_2;
netbios_client_2[source_ip_3] = hostnames_3;
netbios_client_2[source_ip_4] = hostnames_4;
// Build the map of expected results.
HostMap expected_results;
expected_results[hostnames_1[0]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_1[1]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_2[0]] = source_ip_2.ToStringWithoutPort();
expected_results[hostnames_3[0]] = source_ip_3.ToStringWithoutPort();
expected_results[hostnames_4[0]] = source_ip_4.ToStringWithoutPort();
// Add the entry for a NetBios Clients that return packets.
AddNetBiosClient(netbios_client_1);
AddNetBiosClient(netbios_client_2);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(
base::BindOnce(&ExpectResultsEqual, expected_results));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
// Results are not duplicated when multiple interfaces receive the same response
// from one source.
TEST_F(NetBiosHostLocatorTest, MultipleInterfacesWithDuplicateResults) {
// Build data for the first NetBiosClient.
const net::IPEndPoint source_ip_1(net::IPAddress(1, 2, 3, 4), 137);
const Hostnames hostnames_1 = {"hostname1", "HOSTNAME_2"};
const net::IPEndPoint source_ip_2(net::IPAddress(2, 4, 6, 8), 137);
const Hostnames hostnames_2 = {"host.name.3"};
std::map<net::IPEndPoint, Hostnames> netbios_client_1;
netbios_client_1[source_ip_1] = hostnames_1;
netbios_client_1[source_ip_2] = hostnames_2;
// Build data for the second NetBiosClient which also recieves the response
// from |source_ip_2|.
const net::IPEndPoint source_ip_4(net::IPAddress(2, 4, 8, 16), 137);
const Hostnames hostnames_4 = {"hOsTnAmE-5"};
std::map<net::IPEndPoint, Hostnames> netbios_client_2;
netbios_client_2[source_ip_2] = hostnames_2;
netbios_client_2[source_ip_4] = hostnames_4;
// Build the map of expected results.
HostMap expected_results;
expected_results[hostnames_1[0]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_1[1]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_2[0]] = source_ip_2.ToStringWithoutPort();
expected_results[hostnames_4[0]] = source_ip_4.ToStringWithoutPort();
// Add the entry for a NetBios Clients that return packets.
AddNetBiosClient(netbios_client_1);
AddNetBiosClient(netbios_client_2);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(
base::BindOnce(&ExpectResultsEqual, expected_results));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
TEST_F(NetBiosHostLocatorTest, ResultsNotReturnedUntilTimer) {
// Build data for a NetBiosClient
const net::IPEndPoint source_ip_1(net::IPAddress(1, 2, 3, 4), 137);
const Hostnames hostnames_1 = {"hostname1", "HOSTNAME_2"};
const net::IPEndPoint source_ip_2(net::IPAddress(2, 4, 6, 8), 137);
const Hostnames hostnames_2 = {"host.name.3"};
std::map<net::IPEndPoint, Hostnames> netbios_client_1;
netbios_client_1[source_ip_1] = hostnames_1;
netbios_client_1[source_ip_2] = hostnames_2;
// Build the map of expected results.
HostMap expected_results;
expected_results[hostnames_1[0]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_1[1]] = source_ip_1.ToStringWithoutPort();
expected_results[hostnames_2[0]] = source_ip_2.ToStringWithoutPort();
// Add the entry for a NetBios Client that returns packets.
AddNetBiosClient(netbios_client_1);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(std::move(set_true_on_returned_callback_));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds) -
base::TimeDelta::FromMilliseconds(1));
EXPECT_FALSE(has_returned_);
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
EXPECT_TRUE(has_returned_);
}
TEST_F(NetBiosHostLocatorTest, NoValidInterfacesReturnsNoResults) {
auto get_invalid_interfaces_ =
base::BindRepeating(&GenerateInvalidInterfaceList, 3 /* num */);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_invalid_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(base::BindOnce(&ExpectFailure));
}
TEST_F(NetBiosHostLocatorTest, SecondIPUsedForResults) {
const std::string duplicate_hostname = "duplicate";
// Build data for the first NetBiosClient.
const net::IPEndPoint source_ip_1(net::IPAddress(1, 2, 3, 4), 137);
const Hostnames hostnames_1 = {duplicate_hostname};
std::map<net::IPEndPoint, Hostnames> netbios_client_1;
netbios_client_1[source_ip_1] = hostnames_1;
// Build data for the second NetBiosClient which also recieves the response
// from |source_ip_2|.
const net::IPEndPoint source_ip_2(net::IPAddress(2, 4, 8, 16), 137);
const Hostnames hostnames_2 = {duplicate_hostname};
std::map<net::IPEndPoint, Hostnames> netbios_client_2;
netbios_client_2[source_ip_2] = hostnames_2;
// Build the map of expected results.
HostMap expected_results;
expected_results[duplicate_hostname] = source_ip_2.ToStringWithoutPort();
// Add the entry for a NetBios Clients that return packets.
AddNetBiosClient(netbios_client_1);
AddNetBiosClient(netbios_client_2);
host_locator_ = std::make_unique<NetBiosHostLocator>(
get_interfaces_, client_factory_, fake_provider_client_.get(),
std::move(timer_));
host_locator_->FindHosts(
base::BindOnce(&ExpectResultsEqual, expected_results));
task_runner_->FastForwardBy(
base::TimeDelta::FromSeconds(kNetBiosDiscoveryTimeoutSeconds));
}
} // namespace smb_client
} // namespace chromeos