blob: fa48b697fc0bf9af9fcf811c0045da6404dcd795 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/proxy_auto_config_library.h"
#include <algorithm>
#include <deque>
#include <memory>
#include "base/barrier_closure.h"
#include "base/containers/circular_deque.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/address_list.h"
#include "net/base/completion_once_callback.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/base/network_interfaces.h"
#include "net/dns/host_resolver_system_task.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/datagram_client_socket.h"
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
class MockClient : public proxy_resolver::mojom::HostResolverRequestClient {
public:
MockClient(
mojo::PendingReceiver<proxy_resolver::mojom::HostResolverRequestClient>
pending_receiver,
base::OnceClosure results_callback)
: receiver_(this, std::move(pending_receiver)),
results_callback_(std::move(results_callback)) {}
void ReportResult(int error, const net::IPAddressList& results) override {
results_ = results;
std::move(results_callback_).Run();
}
net::IPAddressList GetResults() const { return results_; }
private:
mojo::Receiver<proxy_resolver::mojom::HostResolverRequestClient> receiver_;
base::OnceClosure results_callback_;
net::IPAddressList results_;
};
// Helper for verifying whether the address list returned by myIpAddress() /
// myIpAddressEx() looks correct.
void VerifyActualMyIpAddresses(const net::IPAddressList& test_list) {
// Enumerate all of the IP addresses for the system (skipping loopback and
// link-local ones). This is used as a reference implementation to check
// whether `test_list` (which was obtained using a different strategy) looks
// correct.
std::set<net::IPAddress> candidates;
net::NetworkInterfaceList networks;
net::GetNetworkList(&networks, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
for (const auto& network : networks) {
if (network.address.IsLinkLocal() || network.address.IsLoopback())
continue;
candidates.insert(network.address);
}
EXPECT_GT(test_list.size(), 0u);
// Ordinarily the machine running this test will have an IP address. However
// for some bot configurations (notably Android) that may not be the case.
if (candidates.empty()) {
// There are no possible candidates, so myIpAddress() should return IPV4
// localhost.
ASSERT_EQ(1u, test_list.size());
EXPECT_EQ("127.0.0.1", test_list.front().ToString());
return;
}
// `test_list` should be a subset of `candidates`.
for (const auto& ip : test_list)
EXPECT_EQ(1u, candidates.count(ip));
}
net::IPAddress CreateIPAddress(base::StringPiece literal) {
net::IPAddress result;
if (!result.AssignFromIPLiteral(literal)) {
ADD_FAILURE() << "Failed parsing IP: " << literal;
return net::IPAddress();
}
return result;
}
class MockHostResolverProc : public net::HostResolverProc {
public:
MockHostResolverProc() : HostResolverProc(nullptr) {}
void SetDnsResult(const std::vector<base::StringPiece>& ip_literals) {
result_.clear();
for (const auto& ip : ip_literals)
result_.push_back(net::IPEndPoint(CreateIPAddress(ip), 8080));
}
int Resolve(const std::string& hostname,
net::AddressFamily address_family,
net::HostResolverFlags host_resolver_flags,
net::AddressList* addrlist,
int* os_error) override {
EXPECT_EQ(hostname, net::GetHostName());
*addrlist = result_;
return net::OK;
}
private:
~MockHostResolverProc() override = default;
net::AddressList result_;
};
class MockUDPSocket : public net::DatagramClientSocket {
public:
MockUDPSocket(const net::IPAddress& peer_ip,
const net::IPAddress& local_ip,
net::Error connect_error)
: peer_ip_(peer_ip), local_ip_(local_ip), connect_error_(connect_error) {}
MockUDPSocket(const MockUDPSocket&) = delete;
MockUDPSocket& operator=(const MockUDPSocket&) = delete;
~MockUDPSocket() override = default;
// Socket implementation.
int Read(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) override {
ADD_FAILURE() << "Called Read()";
return net::ERR_UNEXPECTED;
}
int Write(
net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
ADD_FAILURE() << "Called Read()";
return net::ERR_UNEXPECTED;
}
int SetReceiveBufferSize(int32_t size) override {
ADD_FAILURE() << "Called SetReceiveBufferSize()";
return net::ERR_UNEXPECTED;
}
int SetSendBufferSize(int32_t size) override {
ADD_FAILURE() << "Called SetSendBufferSize()";
return net::ERR_UNEXPECTED;
}
// net::DatagramSocket implementation.
void Close() override { ADD_FAILURE() << "Called Close()"; }
int GetPeerAddress(net::IPEndPoint* address) const override {
ADD_FAILURE() << "Called GetPeerAddress()";
return net::ERR_UNEXPECTED;
}
int GetLocalAddress(net::IPEndPoint* address) const override {
if (connect_error_ != net::OK)
return connect_error_;
*address = net::IPEndPoint(local_ip_, 8080);
return net::OK;
}
void UseNonBlockingIO() override {
ADD_FAILURE() << "Called UseNonBlockingIO()";
}
int SetDoNotFragment() override {
ADD_FAILURE() << "Called SetDoNotFragment()";
return net::ERR_UNEXPECTED;
}
void SetMsgConfirm(bool confirm) override {
ADD_FAILURE() << "Called SetMsgConfirm()";
}
const net::NetLogWithSource& NetLog() const override {
ADD_FAILURE() << "Called net::NetLog()";
return net_log_;
}
// net::DatagramClientSocket implementation.
int Connect(const net::IPEndPoint& address) override {
EXPECT_FALSE(connect_async_);
EXPECT_EQ(peer_ip_.ToString(), address.address().ToString());
return connect_error_;
}
int ConnectUsingNetwork(net::handles::NetworkHandle network,
const net::IPEndPoint& address) override {
ADD_FAILURE() << "Called ConnectUsingNetwork()";
return net::ERR_UNEXPECTED;
}
int ConnectUsingDefaultNetwork(const net::IPEndPoint& address) override {
ADD_FAILURE() << "Called ConnectUsingDefaultNetwork()";
return net::ERR_UNEXPECTED;
}
int ConnectAsync(const net::IPEndPoint& address,
net::CompletionOnceCallback callback) override {
if (peer_ip_.IsValid()) {
EXPECT_EQ(peer_ip_.ToString(), address.address().ToString());
}
if (connect_async_) {
if (connect_callback_) {
*connect_callback_ =
base::BindOnce(std::move(callback), connect_error_);
connect_callback_ = nullptr;
}
return net::ERR_IO_PENDING;
}
return connect_error_;
}
int ConnectUsingNetworkAsync(net::handles::NetworkHandle network,
const net::IPEndPoint& address,
net::CompletionOnceCallback callback) override {
ADD_FAILURE() << "Called ConnectUsingNetworkAsync()";
return net::ERR_UNEXPECTED;
}
int ConnectUsingDefaultNetworkAsync(
const net::IPEndPoint& address,
net::CompletionOnceCallback callback) override {
ADD_FAILURE() << "Called ConnectUsingDefaultNetworkAsync()";
return net::ERR_UNEXPECTED;
}
net::handles::NetworkHandle GetBoundNetwork() const override {
ADD_FAILURE() << "Called GetBoundNetwork()";
return network_;
}
void ApplySocketTag(const net::SocketTag& tag) override {
ADD_FAILURE() << "Called ApplySocketTag()";
}
int SetMulticastInterface(uint32_t interface_index) override {
ADD_FAILURE() << "Called SetMulticastInterface()";
return net::ERR_UNEXPECTED;
}
// When ConnectAsync() is called, it should return ERR_IO_PENDING and store
// the callback in `*connect_callback_`. This callback can be run later by
// test code by calling `*connect_callback_` (by running
// MockSocketFactory::RunAsyncConnectCallbacks()).
void SetAsyncConnect(base::OnceClosure* connect_callback) {
connect_async_ = true;
connect_callback_ = connect_callback;
}
private:
net::NetLogWithSource net_log_;
net::handles::NetworkHandle network_;
net::IPAddress peer_ip_;
net::IPAddress local_ip_;
net::Error connect_error_;
bool connect_async_ = false;
raw_ptr<base::OnceClosure> connect_callback_;
};
class MockSocketFactory : public net::ClientSocketFactory {
public:
MockSocketFactory() = default;
// Connect successes and failures that complete asynchronously
void AddUDPConnectSuccess(base::StringPiece peer_ip_literal,
base::StringPiece local_ip_literal,
int connect_order = -1) {
auto peer_ip = CreateIPAddress(peer_ip_literal);
auto local_ip = CreateIPAddress(local_ip_literal);
// The address family of local and peer IP must match.
ASSERT_EQ(peer_ip.size(), local_ip.size());
AddUDPConnectResult(std::move(peer_ip), std::move(local_ip), net::OK,
connect_order);
}
void AddUDPConnectFailure(base::StringPiece peer_ip, int connect_order = -1) {
AddUDPConnectResult(CreateIPAddress(peer_ip), net::IPAddress(),
net::ERR_ADDRESS_UNREACHABLE, connect_order);
}
MockSocketFactory(const MockSocketFactory&) = delete;
MockSocketFactory& operator=(const MockSocketFactory&) = delete;
~MockSocketFactory() override {
if (must_use_all_sockets_) {
EXPECT_EQ(0u, udp_sockets_.size())
<< "Not all of the mock sockets were consumed.";
}
}
// net::ClientSocketFactory
std::unique_ptr<net::DatagramClientSocket> CreateDatagramClientSocket(
net::DatagramSocket::BindType bind_type,
net::NetLog* net_log,
const net::NetLogSource& source) override {
if (udp_sockets_.empty()) {
// If we don't have a result for this one, return a socket that never
// connects (because it is set to connect aysnchronously and isn't added
// to `udp_socket_ptrs`).
// It should be deleted when MyIpAddressImpl is deleted.
auto socket = std::make_unique<MockUDPSocket>(
net::IPAddress(), net::IPAddress(), net::ERR_IO_PENDING);
socket->SetAsyncConnect(nullptr);
return socket;
}
auto result = std::move(udp_sockets_.front());
udp_sockets_.erase(udp_sockets_.begin());
return result;
}
std::unique_ptr<net::TransportClientSocket> CreateTransportClientSocket(
const net::AddressList& addresses,
std::unique_ptr<net::SocketPerformanceWatcher> socket_performance_watcher,
net::NetworkQualityEstimator* network_quality_estimator,
net::NetLog* net_log,
const net::NetLogSource& source) override {
ADD_FAILURE() << "Called CreateTransportClientSocket()";
return nullptr;
}
std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
net::SSLClientContext* context,
std::unique_ptr<net::StreamSocket> stream_socket,
const net::HostPortPair& host_and_port,
const net::SSLConfig& ssl_config) override {
ADD_FAILURE() << "Called CreateSSLClientSocket()";
return nullptr;
}
// Used to test async-connected sockets. Returns false if we are not finished
// running callbacks.
bool RunAsyncConnectCallbacks() {
while (!connect_callbacks_.empty()) {
base::OnceClosure& first_callback = connect_callbacks_.front();
if (!first_callback)
return false;
std::move(first_callback).Run();
connect_callbacks_.pop_front();
}
return true;
}
void SetCanLeaveSocketsUnused() { must_use_all_sockets_ = false; }
private:
void AddUDPConnectResult(net::IPAddress peer_ip,
net::IPAddress local_ip,
net::Error net_error,
int connect_order) {
auto socket = std::make_unique<MockUDPSocket>(
std::move(peer_ip), std::move(local_ip), net_error);
if (connect_order >= 0) {
connect_callbacks_.resize(
std::max(size_t(connect_order + 1), connect_callbacks_.size()));
CHECK(!connect_callbacks_[connect_order]);
socket->SetAsyncConnect(&connect_callbacks_[connect_order]);
}
udp_sockets_.push_back(std::move(socket));
}
std::vector<std::unique_ptr<MockUDPSocket>> udp_sockets_;
// Connection callbacks for the sockets, in order of async connection
// completion.
std::deque<base::OnceClosure> connect_callbacks_;
// Unit tests should always consume all of the mock UDP sockets unless this is
// set to false.
bool must_use_all_sockets_ = true;
};
class PacLibraryTest : public testing::Test {
public:
PacLibraryTest()
: host_resolver_proc_(base::MakeRefCounted<MockHostResolverProc>()) {
net::EnsureSystemHostResolverCallReady();
}
~PacLibraryTest() override = default;
protected:
net::IPAddressList PacMyIpAddressForTest() {
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
return RunMyIpAddressUntilCompletion();
}
net::IPAddressList PacMyIpAddressExForTest() {
impl_ = std::make_unique<MyIpAddressImpl>(
MyIpAddressImpl::Mode::kMyIpAddressEx);
return RunMyIpAddressUntilCompletion();
}
net::IPAddressList RunMyIpAddressUntilCompletion() {
RunAsyncConnectCallbacksAndPostAgain();
if (use_mocks_) {
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
}
base::RunLoop run_loop;
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient>
remote;
MockClient client(remote.InitWithNewPipeAndPassReceiver(),
run_loop.QuitClosure());
impl_->AddRequest(std::move(remote));
run_loop.Run();
return client.GetResults();
}
void RunAsyncConnectCallbacksAndPostAgain() {
bool finished = factory_.RunAsyncConnectCallbacks();
// If all the ConnectAsync() completion callbacks haven't been called yet
// they may need to in the future.
if (!finished) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&PacLibraryTest::RunAsyncConnectCallbacksAndPostAgain,
base::Unretained(this)));
}
}
void SetRealTest() { use_mocks_ = false; }
base::test::TaskEnvironment task_environment_;
std::unique_ptr<MyIpAddressImpl> impl_;
MockSocketFactory factory_;
scoped_refptr<MockHostResolverProc> host_resolver_proc_;
bool use_mocks_ = true;
};
// Tests for actual PacMyIpAddress() and PacMyIpAddressEx() (real socket
// connections and DNS results rather than mocks)
TEST_F(PacLibraryTest, ActualPacMyIpAddress) {
SetRealTest();
auto my_ip_addresses = PacMyIpAddressForTest();
VerifyActualMyIpAddresses(my_ip_addresses);
}
TEST_F(PacLibraryTest, ActualPacMyIpAddressEx) {
SetRealTest();
auto my_ip_addresses = PacMyIpAddressExForTest();
VerifyActualMyIpAddresses(my_ip_addresses);
}
// Tests myIpAddress() when there is a route to 8.8.8.8.
TEST_F(PacLibraryTest, PacMyIpAddress8888) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
TEST_F(PacLibraryTest, PacMyIpAddress8888AsyncConnect) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
// Tests successful async-completion of the connections.
TEST_F(PacLibraryTest, PacMyIpAddress8888AsyncConnect2) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef", 1);
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
TEST_F(PacLibraryTest, PacMyIpAddress8888AsyncConnect2OutOfOrder) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 1);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef", 0);
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
TEST_F(PacLibraryTest, PacMyIpAddress8888AsyncConnect2OutOfOrder2) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
// Tests myIpAddress() when there is no route to 8.8.8.8, but there is one to
// 2001:4860:4860::8888.
TEST_F(PacLibraryTest, PacMyIpAddress2001) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2001::beef");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("2001::beef", result.front().ToString());
}
// Tests myIpAddress() when there is no route to 8.8.8.8, no route to
// 2001:4860:4860::8888, however getaddrinfo(gethostname()) finds results. Most
// of those results are skipped over, and the IPv4 one is favored.
TEST_F(PacLibraryTest, PacMyIpAddressHostname) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"127.0.0.1",
"::1",
"fe89::beef",
"2001::f001",
"178.1.99.3",
"192.168.1.3",
});
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("178.1.99.3", result.front().ToString());
}
// Tests myIpAddress() when there is no route to 8.8.8.8, no route to
// 2001:4860:4860::8888, however getaddrinfo(gethostname()) finds multiple IPv6
// results.
TEST_F(PacLibraryTest, PacMyIpAddressHostnameAllIPv6) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult(
{"::1", "2001::f001", "2001::f00d", "169.254.0.6"});
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("2001::f001", result.front().ToString());
}
// Tests myIpAddress() when there is no route to 8.8.8.8, no route to
// 2001:4860:4860::8888, no acceptable result in getaddrinfo(gethostname()),
// however there is a route for private address.
TEST_F(PacLibraryTest, PacMyIpAddressPrivateIPv4) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"127.0.0.1",
"::1",
"fe89::beef",
});
factory_.AddUDPConnectSuccess("10.0.0.0", "127.0.0.1");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectSuccess("192.168.0.0", "63.31.9.8");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("63.31.9.8", result.front().ToString());
}
// Tests myIpAddress() when there is no route to 8.8.8.8, no route to
// 2001:4860:4860::8888, no acceptable result in getaddrinfo(gethostname()),
// however there is a route for private address.
TEST_F(PacLibraryTest, PacMyIpAddressPrivateIPv6) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
// No DNS result
factory_.AddUDPConnectSuccess("10.0.0.0", "127.0.0.1");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectSuccess("FC00::", "2001::7777");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("2001::7777", result.front().ToString());
}
// Tests myIpAddress() when there are no routes, and getaddrinfo(gethostname())
// fails.
TEST_F(PacLibraryTest, PacMyIpAddressAllFail) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
// No DNS result
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressForTest();
// Every method failed, so myIpAddress() should return IPV4 localhost.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("127.0.0.1", result.front().ToString());
}
// Tests myIpAddress() when there are no routes, and
// getaddrinfo(gethostname()) only returns loopback.
TEST_F(PacLibraryTest, PacMyIpAddressAllFailOrLoopback) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({"127.0.0.1", "::1"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressForTest();
// Every method failed, so myIpAddress() should return IPV4 localhost.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("127.0.0.1", result.front().ToString());
}
// Tests myIpAddress() when there is only an IPv6 link-local address.
TEST_F(PacLibraryTest, PacMyIpAddressAllFailHasLinkLocal) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({"127.0.0.1", "::1", "fe81::8881"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("fe81::8881", result.front().ToString());
}
// Tests myIpAddress() when there are only link-local addresses. The IPv4
// link-local address is favored.
TEST_F(PacLibraryTest, PacMyIpAddressAllFailHasLinkLocalFavorIPv4) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult(
{"127.0.0.1", "::1", "fe81::8881", "169.254.89.133"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("169.254.89.133", result.front().ToString());
}
// Tests myIpAddressEx() when there is a route to 8.8.8.8 but not one to
// 2001:4860:4860::8888
TEST_F(PacLibraryTest, PacMyIpAddressEx8888) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
auto result = PacMyIpAddressExForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("192.168.1.1", result.front().ToString());
}
// Tests myIpAddressEx() when there is a route to 2001:4860:4860::8888 but
// not 8.8.8.8.
TEST_F(PacLibraryTest, PacMyIpAddressEx2001) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2001::3333");
auto result = PacMyIpAddressExForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("2001::3333", result.front().ToString());
}
// Tests myIpAddressEx() when there is a route to both 8.8.8.8 and
// 2001:4860:4860::8888.
TEST_F(PacLibraryTest, PacMyIpAddressEx8888And2001) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.17.8");
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2001::8333");
auto result = PacMyIpAddressExForTest();
ASSERT_EQ(2u, result.size());
EXPECT_EQ("192.168.17.8", result.front().ToString());
EXPECT_EQ("2001::8333", result.back().ToString());
}
// Tests myIpAddressEx() when there is no route to 8.8.8.8, no route to
// 2001:4860:4860::8888, however getaddrinfo(gethostname()) finds results. Some
// of those results are skipped due to being link-local and loopback.
TEST_F(PacLibraryTest, PacMyIpAddressExHostname) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"::1",
"fe89::beef",
"2001::bebe",
"178.1.99.3",
"127.0.0.1",
"192.168.1.3",
});
auto result = PacMyIpAddressExForTest();
ASSERT_EQ(3u, result.size());
EXPECT_EQ("2001::bebe", result[0].ToString());
EXPECT_EQ("178.1.99.3", result[1].ToString());
EXPECT_EQ("192.168.1.3", result[2].ToString());
}
// Tests myIpAddressEx() when routes are found for private IP space.
TEST_F(PacLibraryTest, PacMyIpAddressExPrivateDuplicates) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
// No DNS result
factory_.AddUDPConnectSuccess("10.0.0.0", "192.168.3.3");
factory_.AddUDPConnectSuccess("172.16.0.0", "192.168.3.4");
factory_.AddUDPConnectSuccess("192.168.0.0", "192.168.3.3");
factory_.AddUDPConnectSuccess("FC00::", "2001::beef");
auto result = PacMyIpAddressExForTest();
// Note that 192.168.3.3. was probed twice, but only added once to the final
// result.
ASSERT_EQ(3u, result.size());
EXPECT_EQ("192.168.3.3", result[0].ToString());
EXPECT_EQ("192.168.3.4", result[1].ToString());
EXPECT_EQ("2001::beef", result[2].ToString());
}
// Tests the same as above, but some of the connections complete asynchronously.
TEST_F(PacLibraryTest, PacMyIpAddressExPrivateDuplicatesAsyncConnect) {
factory_.AddUDPConnectFailure("8.8.8.8", 1);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 0);
// No DNS result
factory_.AddUDPConnectSuccess("10.0.0.0", "192.168.3.3", 4);
factory_.AddUDPConnectSuccess("172.16.0.0", "192.168.3.4", 2);
factory_.AddUDPConnectSuccess("192.168.0.0", "192.168.3.3", 5);
factory_.AddUDPConnectSuccess("FC00::", "2001::beef", 3);
auto result = PacMyIpAddressExForTest();
// Note that 192.168.3.3. was probed twice, but only added once to the final
// result.
ASSERT_EQ(3u, result.size());
EXPECT_EQ("192.168.3.3", result[0].ToString());
EXPECT_EQ("192.168.3.4", result[1].ToString());
EXPECT_EQ("2001::beef", result[2].ToString());
}
// Tests myIpAddressEx() when there are no routes, and
// getaddrinfo(gethostname()) fails.
TEST_F(PacLibraryTest, PacMyIpAddressExAllFail) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
// No DNS result
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressExForTest();
// Every method failed, so myIpAddress() should return IPV4 localhost.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("127.0.0.1", result.front().ToString());
}
// Tests myIpAddressEx() when there are only IPv6 link-local address.
TEST_F(PacLibraryTest, PacMyIpAddressExAllFailHasLinkLocal) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult(
{"127.0.0.1", "::1", "fe81::8881", "fe80::8899"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectSuccess("FC00::", "fe80::1");
auto result = PacMyIpAddressExForTest();
// There were four link-local addresses found, but only the first one is
// returned.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("fe81::8881", result.front().ToString());
}
// Tests myIpAddressEx() when there are only link-local addresses. The IPv4
// link-local address is favored.
TEST_F(PacLibraryTest, PacMyIpAddressExAllFailHasLinkLocalFavorIPv4) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult(
{"127.0.0.1", "::1", "fe81::8881", "169.254.89.133"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressExForTest();
ASSERT_EQ(1u, result.size());
EXPECT_EQ("169.254.89.133", result.front().ToString());
}
// Tests myIpAddressEx() when there are no routes, and
// getaddrinfo(gethostname()) only returns loopback.
TEST_F(PacLibraryTest, PacMyIpAddressExAllFailOrLoopback) {
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({"127.0.0.1", "::1"});
factory_.AddUDPConnectFailure("10.0.0.0");
factory_.AddUDPConnectFailure("172.16.0.0");
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::");
auto result = PacMyIpAddressExForTest();
// Every method failed, so myIpAddress() should return IPV4 localhost.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("127.0.0.1", result.front().ToString());
}
TEST_F(PacLibraryTest, PacMyIpAddressExRunMultipleTimes) {
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddressEx);
// Run the PacMyIpAddressExHostname test.
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"::1",
"fe89::beef",
"2001::bebe",
"178.1.99.3",
"127.0.0.1",
"192.168.1.3",
});
auto result = RunMyIpAddressUntilCompletion();
ASSERT_EQ(3u, result.size());
EXPECT_EQ("2001::bebe", result[0].ToString());
EXPECT_EQ("178.1.99.3", result[1].ToString());
EXPECT_EQ("192.168.1.3", result[2].ToString());
// Run the PacMyIpAddressExPrivateDuplicates with the same `impl_` as the
// previous test.
factory_.AddUDPConnectFailure("8.8.8.8");
factory_.AddUDPConnectFailure("2001:4860:4860::8888");
host_resolver_proc_->SetDnsResult({});
factory_.AddUDPConnectSuccess("10.0.0.0", "192.168.3.3");
factory_.AddUDPConnectSuccess("172.16.0.0", "192.168.3.4");
factory_.AddUDPConnectSuccess("192.168.0.0", "192.168.3.3");
factory_.AddUDPConnectSuccess("FC00::", "2001::beef");
result = RunMyIpAddressUntilCompletion();
ASSERT_EQ(3u, result.size());
EXPECT_EQ("192.168.3.3", result[0].ToString());
EXPECT_EQ("192.168.3.4", result[1].ToString());
EXPECT_EQ("2001::beef", result[2].ToString());
}
TEST_F(PacLibraryTest, PacMyIpAddressExRunMultipleTimesAsync) {
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddressEx);
// Run the PacMyIpAddressExHostname test.
factory_.AddUDPConnectFailure("8.8.8.8", 1);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 0);
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"::1",
"fe89::beef",
"2001::bebe",
"178.1.99.3",
"127.0.0.1",
"192.168.1.3",
});
auto result = RunMyIpAddressUntilCompletion();
ASSERT_EQ(3u, result.size());
EXPECT_EQ("2001::bebe", result[0].ToString());
EXPECT_EQ("178.1.99.3", result[1].ToString());
EXPECT_EQ("192.168.1.3", result[2].ToString());
// Results for the second test.
factory_.AddUDPConnectFailure("8.8.8.8", 1);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 0);
host_resolver_proc_->SetDnsResult({});
factory_.AddUDPConnectSuccess("10.0.0.0", "192.168.3.3", 2);
factory_.AddUDPConnectSuccess("172.16.0.0", "192.168.3.4", 3);
factory_.AddUDPConnectSuccess("192.168.0.0", "192.168.3.3", 4);
factory_.AddUDPConnectSuccess("FC00::", "2001::beef", 5);
// Run the PacMyIpAddressExPrivateDuplicates with the same `impl_` as the
// previous test.
result = RunMyIpAddressUntilCompletion();
ASSERT_EQ(3u, result.size());
EXPECT_EQ("192.168.3.3", result[0].ToString());
EXPECT_EQ("192.168.3.4", result[1].ToString());
EXPECT_EQ("2001::beef", result[2].ToString());
}
// Same as above but the connections complete asynchronously.
TEST_F(PacLibraryTest, PacMyIpAddressExAllFailOrLoopbackAsyncConnect) {
factory_.AddUDPConnectFailure("8.8.8.8", 0);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 1);
host_resolver_proc_->SetDnsResult({"127.0.0.1", "::1"});
factory_.AddUDPConnectFailure("10.0.0.0", 2);
factory_.AddUDPConnectFailure("172.16.0.0", 3);
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::", 4);
auto result = PacMyIpAddressExForTest();
// Every method failed, so myIpAddress() should return IPV4 localhost.
ASSERT_EQ(1u, result.size());
EXPECT_EQ("127.0.0.1", result.front().ToString());
}
// Tests that during async connect, MyIpAddressImpl can be deleted successfully.
TEST_F(PacLibraryTest, DeleteMyIpAddressImpl) {
factory_.AddUDPConnectFailure("8.8.8.8", 1);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 0);
host_resolver_proc_->SetDnsResult({
"169.254.13.16",
"127.0.0.1",
"::1",
"fe89::beef",
});
factory_.AddUDPConnectSuccess("10.0.0.0", "127.0.0.1", 2);
factory_.AddUDPConnectFailure("172.16.0.0", 3);
factory_.AddUDPConnectSuccess("192.168.0.0", "63.31.9.8", 4);
// The `impl_` doesn't actually use any of these sockets before it's deleted.
factory_.SetCanLeaveSocketsUnused();
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
// Post a task that deletes `impl_`.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() { impl_.reset(); }));
// Then post a task that runs the async connection callbacks.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
base::IgnoreResult(&MockSocketFactory::RunAsyncConnectCallbacks),
base::Unretained(&factory_)));
// Now start the gathering.
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote;
// NOTREACHED() because the request below should never complete.
MockClient client(remote.InitWithNewPipeAndPassReceiver(),
base::BindOnce([]() { NOTREACHED(); }));
impl_->AddRequest(std::move(remote));
// Once all the tasks are run, `impl_` is guaranteed to be deleted.
task_environment_.RunUntilIdle();
CHECK(!impl_);
}
TEST_F(PacLibraryTest, ConnectMultipleRemotes) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef", 1);
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
base::RunLoop run_loop;
// Don't call the RunLoop's QuitClosure until both clients are done.
base::RepeatingClosure results_cb =
base::BarrierClosure(2, run_loop.QuitClosure());
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote1;
MockClient client1(remote1.InitWithNewPipeAndPassReceiver(), results_cb);
impl_->AddRequest(std::move(remote1));
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote2;
MockClient client2(remote2.InitWithNewPipeAndPassReceiver(), results_cb);
impl_->AddRequest(std::move(remote2));
// Connections happen asynchronously so post a task to respond to connection
// requests.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting(
[&]() { factory_.RunAsyncConnectCallbacks(); }));
// Runs until both clients have received results.
run_loop.Run();
net::IPAddressList result1 = client1.GetResults();
ASSERT_EQ(1u, result1.size());
EXPECT_EQ("192.168.1.1", result1.front().ToString());
net::IPAddressList result2 = client2.GetResults();
EXPECT_EQ(result1, result2);
}
// A clone of the above test that connects the second client after
// MyIpAddressImpl has lready started running.
TEST_F(PacLibraryTest, ConnectMultipleRemotesAsync) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef", 1);
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
base::RunLoop run_loop;
// Don't call the RunLoop's QuitClosure until both clients are done.
base::RepeatingClosure results_cb =
base::BarrierClosure(2, run_loop.QuitClosure());
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote1;
MockClient client1(remote1.InitWithNewPipeAndPassReceiver(), results_cb);
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote2;
MockClient client2(remote2.InitWithNewPipeAndPassReceiver(), results_cb);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting(
[&]() { impl_->AddRequest(std::move(remote2)); }));
// Connections happen asynchronously so post a task to respond to connection
// requests.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting(
[&]() { factory_.RunAsyncConnectCallbacks(); }));
impl_->AddRequest(std::move(remote1));
// Runs until both clients have received results.
run_loop.Run();
net::IPAddressList result1 = client1.GetResults();
ASSERT_EQ(1u, result1.size());
EXPECT_EQ("192.168.1.1", result1.front().ToString());
net::IPAddressList result2 = client2.GetResults();
EXPECT_EQ(result1, result2);
}
// Connect multiple remotes, but one disconnects.
TEST_F(PacLibraryTest, ConnectMultipleRemotesOneDisconnects) {
factory_.AddUDPConnectSuccess("8.8.8.8", "192.168.1.1", 0);
factory_.AddUDPConnectSuccess("2001:4860:4860::8888", "2002::beef", 1);
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
base::RunLoop run_loop;
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote1;
MockClient client1(remote1.InitWithNewPipeAndPassReceiver(),
run_loop.QuitClosure());
// This second client will be disconnected as MyIpAddressImpl runs and should
// never receive results.
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote2;
std::unique_ptr<MockClient> client2 =
std::make_unique<MockClient>(remote2.InitWithNewPipeAndPassReceiver(),
base::BindOnce([]() { NOTREACHED(); }));
// Post a task that deletes |client2|.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() { client2.reset(); }));
// Connections happen asynchronously so post a task to respond to connection
// requests.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting(
[&]() { factory_.RunAsyncConnectCallbacks(); }));
impl_->AddRequest(std::move(remote1));
impl_->AddRequest(std::move(remote2));
run_loop.Run();
net::IPAddressList result1 = client1.GetResults();
ASSERT_EQ(1u, result1.size());
EXPECT_EQ("192.168.1.1", result1.front().ToString());
EXPECT_FALSE(client2);
}
// Connect multiple remotes, but one disconnects.
TEST_F(PacLibraryTest, ConnectMultipleRemotesButAllDisconnect) {
factory_.AddUDPConnectFailure("8.8.8.8", 0);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 1);
// No DNS result.
factory_.AddUDPConnectFailure("10.0.0.0", 2);
factory_.AddUDPConnectFailure("172.16.0.0", 3);
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::", 4);
// The last 4 sockets will not be used as all of the Remotes will disconnect
// before that point.
factory_.SetCanLeaveSocketsUnused();
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote1;
std::unique_ptr<MockClient> client1 =
std::make_unique<MockClient>(remote1.InitWithNewPipeAndPassReceiver(),
base::BindOnce([]() { NOTREACHED(); }));
// This second client will be disconnected as MyIpAddressImpl runs and should
// never receive results.
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote2;
std::unique_ptr<MockClient> client2 =
std::make_unique<MockClient>(remote2.InitWithNewPipeAndPassReceiver(),
base::BindOnce([]() { NOTREACHED(); }));
// Post a task that deletes |client1|.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() { client1.reset(); }));
// Post a task to respond to connection requests.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting(
[&]() { factory_.RunAsyncConnectCallbacks(); }));
// Post a task that deletes |client2|.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() { client2.reset(); }));
impl_->AddRequest(std::move(remote1));
impl_->AddRequest(std::move(remote2));
// Can't reasonably use a RunLoop here because deleting the clients will post
// disconnection callbacks and we want those to run.
task_environment_.RunUntilIdle();
EXPECT_FALSE(client1);
EXPECT_FALSE(client2);
}
// Connect one remote, and during search, disconnect one remote but connect
// another.
TEST_F(PacLibraryTest, ConnectOneRemoteAndThenAnother) {
factory_.AddUDPConnectFailure("8.8.8.8", 0);
factory_.AddUDPConnectFailure("2001:4860:4860::8888", 1);
// No DNS result.
factory_.AddUDPConnectFailure("10.0.0.0", 2);
factory_.AddUDPConnectFailure("172.16.0.0", 3);
factory_.AddUDPConnectFailure("192.168.0.0");
factory_.AddUDPConnectFailure("FC00::", 4);
impl_ =
std::make_unique<MyIpAddressImpl>(MyIpAddressImpl::Mode::kMyIpAddress);
impl_->SetSocketFactoryForTest(&factory_);
impl_->SetHostResolverProcForTest(host_resolver_proc_);
base::RunLoop run_loop;
// This client will be disconnected as MyIpAddressImpl runs and should never
// receive results.
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote1;
std::unique_ptr<MockClient> client1 =
std::make_unique<MockClient>(remote1.InitWithNewPipeAndPassReceiver(),
base::BindOnce([]() { NOTREACHED(); }));
// This client will receive the results.
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient> remote2;
std::unique_ptr<MockClient> client2 = std::make_unique<MockClient>(
remote2.InitWithNewPipeAndPassReceiver(), run_loop.QuitClosure());
// Post a task that deletes |client1| but connects |client2|.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
client1.reset();
impl_->AddRequest(std::move(remote2));
}));
// Post a task to respond to connection requests.
RunAsyncConnectCallbacksAndPostAgain();
impl_->AddRequest(std::move(remote1));
run_loop.Run();
EXPECT_FALSE(client1);
net::IPAddressList result2 = client2->GetResults();
ASSERT_EQ(1u, result2.size());
EXPECT_EQ("127.0.0.1", result2.front().ToString());
}
} // namespace
} // namespace network