547 lines
23 KiB
C++
547 lines
23 KiB
C++
|
|
// Copyright 2019 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "net/socket/socks_connect_job.h"
|
||
|
|
|
||
|
|
#include "base/containers/flat_set.h"
|
||
|
|
#include "base/containers/span.h"
|
||
|
|
#include "base/functional/callback.h"
|
||
|
|
#include "base/run_loop.h"
|
||
|
|
#include "base/test/task_environment.h"
|
||
|
|
#include "base/time/time.h"
|
||
|
|
#include "build/build_config.h"
|
||
|
|
#include "net/base/load_states.h"
|
||
|
|
#include "net/base/load_timing_info.h"
|
||
|
|
#include "net/base/load_timing_info_test_util.h"
|
||
|
|
#include "net/base/net_errors.h"
|
||
|
|
#include "net/base/network_isolation_key.h"
|
||
|
|
#include "net/dns/mock_host_resolver.h"
|
||
|
|
#include "net/dns/public/secure_dns_policy.h"
|
||
|
|
#include "net/log/net_log.h"
|
||
|
|
#include "net/socket/client_socket_factory.h"
|
||
|
|
#include "net/socket/client_socket_handle.h"
|
||
|
|
#include "net/socket/connect_job_test_util.h"
|
||
|
|
#include "net/socket/socket_tag.h"
|
||
|
|
#include "net/socket/socket_test_util.h"
|
||
|
|
#include "net/socket/socks_connect_job.h"
|
||
|
|
#include "net/socket/transport_client_socket_pool_test_util.h"
|
||
|
|
#include "net/socket/transport_connect_job.h"
|
||
|
|
#include "net/test/gtest_util.h"
|
||
|
|
#include "net/test/test_with_task_environment.h"
|
||
|
|
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
|
||
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
|
||
|
|
namespace net {
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
const char kProxyHostName[] = "proxy.test";
|
||
|
|
const int kProxyPort = 4321;
|
||
|
|
|
||
|
|
constexpr base::TimeDelta kTinyTime = base::Microseconds(1);
|
||
|
|
|
||
|
|
class SOCKSConnectJobTest : public testing::Test, public WithTaskEnvironment {
|
||
|
|
public:
|
||
|
|
enum class SOCKSVersion {
|
||
|
|
V4,
|
||
|
|
V5,
|
||
|
|
};
|
||
|
|
|
||
|
|
SOCKSConnectJobTest()
|
||
|
|
: WithTaskEnvironment(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
|
||
|
|
common_connect_job_params_(
|
||
|
|
&client_socket_factory_,
|
||
|
|
&host_resolver_,
|
||
|
|
nullptr /* http_auth_cache */,
|
||
|
|
nullptr /* http_auth_handler_factory */,
|
||
|
|
nullptr /* spdy_session_pool */,
|
||
|
|
nullptr /* quic_supported_versions */,
|
||
|
|
nullptr /* quic_stream_factory */,
|
||
|
|
nullptr /* proxy_delegate */,
|
||
|
|
nullptr /* http_user_agent_settings */,
|
||
|
|
nullptr /* ssl_client_context */,
|
||
|
|
nullptr /* socket_performance_watcher_factory */,
|
||
|
|
nullptr /* network_quality_estimator */,
|
||
|
|
NetLog::Get(),
|
||
|
|
nullptr /* websocket_endpoint_lock_manager */) {}
|
||
|
|
|
||
|
|
~SOCKSConnectJobTest() override = default;
|
||
|
|
|
||
|
|
static scoped_refptr<SOCKSSocketParams> CreateSOCKSParams(
|
||
|
|
SOCKSVersion socks_version,
|
||
|
|
SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
|
||
|
|
return base::MakeRefCounted<SOCKSSocketParams>(
|
||
|
|
base::MakeRefCounted<TransportSocketParams>(
|
||
|
|
HostPortPair(kProxyHostName, kProxyPort), NetworkAnonymizationKey(),
|
||
|
|
secure_dns_policy, OnHostResolutionCallback(),
|
||
|
|
/*supported_alpns=*/base::flat_set<std::string>()),
|
||
|
|
socks_version == SOCKSVersion::V5,
|
||
|
|
socks_version == SOCKSVersion::V4
|
||
|
|
? HostPortPair(kSOCKS4TestHost, kSOCKS4TestPort)
|
||
|
|
: HostPortPair(kSOCKS5TestHost, kSOCKS5TestPort),
|
||
|
|
NetworkAnonymizationKey(), TRAFFIC_ANNOTATION_FOR_TESTS);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
MockHostResolver host_resolver_{/*default_result=*/MockHostResolverBase::
|
||
|
|
RuleResolver::GetLocalhostResult()};
|
||
|
|
MockTaggingClientSocketFactory client_socket_factory_;
|
||
|
|
const CommonConnectJobParams common_connect_job_params_;
|
||
|
|
};
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, HostResolutionFailure) {
|
||
|
|
host_resolver_.rules()->AddSimulatedTimeoutFailure(kProxyHostName);
|
||
|
|
|
||
|
|
for (bool failure_synchronous : {false, true}) {
|
||
|
|
host_resolver_.set_synchronous_mode(failure_synchronous);
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
test_delegate.StartJobExpectingResult(
|
||
|
|
&socks_connect_job, ERR_PROXY_CONNECTION_FAILED, failure_synchronous);
|
||
|
|
EXPECT_THAT(socks_connect_job.GetResolveErrorInfo().error,
|
||
|
|
test::IsError(ERR_DNS_TIMED_OUT));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, HostResolutionFailureSOCKS4Endpoint) {
|
||
|
|
const char hostname[] = "google.com";
|
||
|
|
host_resolver_.rules()->AddSimulatedTimeoutFailure(hostname);
|
||
|
|
|
||
|
|
for (bool failure_synchronous : {false, true}) {
|
||
|
|
host_resolver_.set_synchronous_mode(failure_synchronous);
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data{base::span<MockRead>(),
|
||
|
|
base::span<MockWrite>()};
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
scoped_refptr<SOCKSSocketParams> socket_params =
|
||
|
|
base::MakeRefCounted<SOCKSSocketParams>(
|
||
|
|
base::MakeRefCounted<TransportSocketParams>(
|
||
|
|
HostPortPair(kProxyHostName, kProxyPort),
|
||
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
||
|
|
OnHostResolutionCallback(),
|
||
|
|
/*supported_alpns=*/base::flat_set<std::string>()),
|
||
|
|
false /* socks_v5 */, HostPortPair(hostname, kSOCKS4TestPort),
|
||
|
|
NetworkAnonymizationKey(), TRAFFIC_ANNOTATION_FOR_TESTS);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(
|
||
|
|
DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
|
||
|
|
socket_params, &test_delegate, nullptr /* net_log */);
|
||
|
|
test_delegate.StartJobExpectingResult(
|
||
|
|
&socks_connect_job, ERR_NAME_NOT_RESOLVED, failure_synchronous);
|
||
|
|
EXPECT_THAT(socks_connect_job.GetResolveErrorInfo().error,
|
||
|
|
test::IsError(ERR_DNS_TIMED_OUT));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, HandshakeError) {
|
||
|
|
for (bool host_resolution_synchronous : {false, true}) {
|
||
|
|
for (bool write_failure_synchronous : {false, true}) {
|
||
|
|
host_resolver_.set_synchronous_mode(host_resolution_synchronous);
|
||
|
|
|
||
|
|
// No need to distinguish which part of the handshake fails. Those details
|
||
|
|
// are all handled at the StreamSocket layer, not the SOCKSConnectJob.
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(write_failure_synchronous ? SYNCHRONOUS : ASYNC,
|
||
|
|
ERR_UNEXPECTED, 0),
|
||
|
|
};
|
||
|
|
SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
|
||
|
|
// Host resolution is used to switch between sync and async connection
|
||
|
|
// behavior. The SOCKS layer can't distinguish between sync and async host
|
||
|
|
// resolution vs sync and async connection establishment, so just always
|
||
|
|
// make connection establishment synchroonous.
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
test_delegate.StartJobExpectingResult(
|
||
|
|
&socks_connect_job, ERR_UNEXPECTED,
|
||
|
|
host_resolution_synchronous && write_failure_synchronous);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, SOCKS4) {
|
||
|
|
for (bool host_resolution_synchronous : {false, true}) {
|
||
|
|
for (bool read_and_writes_synchronous : {true}) {
|
||
|
|
host_resolver_.set_synchronous_mode(host_resolution_synchronous);
|
||
|
|
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(SYNCHRONOUS, kSOCKS4OkRequestLocalHostPort80,
|
||
|
|
kSOCKS4OkRequestLocalHostPort80Length, 0),
|
||
|
|
};
|
||
|
|
|
||
|
|
MockRead reads[] = {
|
||
|
|
MockRead(SYNCHRONOUS, kSOCKS4OkReply, kSOCKS4OkReplyLength, 1),
|
||
|
|
};
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data(reads, writes);
|
||
|
|
// Host resolution is used to switch between sync and async connection
|
||
|
|
// behavior. The SOCKS layer can't distinguish between sync and async host
|
||
|
|
// resolution vs sync and async connection establishment, so just always
|
||
|
|
// make connection establishment synchroonous.
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V4),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
test_delegate.StartJobExpectingResult(
|
||
|
|
&socks_connect_job, OK,
|
||
|
|
host_resolution_synchronous && read_and_writes_synchronous);
|
||
|
|
|
||
|
|
// Proxies should not set any DNS aliases.
|
||
|
|
EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, SOCKS5) {
|
||
|
|
for (bool host_resolution_synchronous : {false, true}) {
|
||
|
|
for (bool read_and_writes_synchronous : {true}) {
|
||
|
|
host_resolver_.set_synchronous_mode(host_resolution_synchronous);
|
||
|
|
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(SYNCHRONOUS, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength,
|
||
|
|
0),
|
||
|
|
MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 2),
|
||
|
|
};
|
||
|
|
|
||
|
|
MockRead reads[] = {
|
||
|
|
MockRead(SYNCHRONOUS, kSOCKS5GreetResponse,
|
||
|
|
kSOCKS5GreetResponseLength, 1),
|
||
|
|
MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 3),
|
||
|
|
};
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data(reads, writes);
|
||
|
|
// Host resolution is used to switch between sync and async connection
|
||
|
|
// behavior. The SOCKS layer can't distinguish between sync and async host
|
||
|
|
// resolution vs sync and async connection establishment, so just always
|
||
|
|
// make connection establishment synchroonous.
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
test_delegate.StartJobExpectingResult(
|
||
|
|
&socks_connect_job, OK,
|
||
|
|
host_resolution_synchronous && read_and_writes_synchronous);
|
||
|
|
|
||
|
|
// Proxies should not set any DNS aliases.
|
||
|
|
EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, HasEstablishedConnection) {
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(ASYNC, kSOCKS4OkRequestLocalHostPort80,
|
||
|
|
kSOCKS4OkRequestLocalHostPort80Length, 0),
|
||
|
|
};
|
||
|
|
|
||
|
|
MockRead reads[] = {
|
||
|
|
MockRead(ASYNC, ERR_IO_PENDING, 1),
|
||
|
|
MockRead(ASYNC, kSOCKS4OkReply, kSOCKS4OkReplyLength, 2),
|
||
|
|
};
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data(reads, writes);
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(ASYNC, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V4),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job.Connect();
|
||
|
|
EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, socks_connect_job.GetLoadState());
|
||
|
|
EXPECT_FALSE(socks_connect_job.HasEstablishedConnection());
|
||
|
|
|
||
|
|
host_resolver_.ResolveNow(1);
|
||
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, socks_connect_job.GetLoadState());
|
||
|
|
EXPECT_FALSE(socks_connect_job.HasEstablishedConnection());
|
||
|
|
|
||
|
|
sequenced_socket_data.RunUntilPaused();
|
||
|
|
// "LOAD_STATE_CONNECTING" is also returned when negotiating a SOCKS
|
||
|
|
// connection.
|
||
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, socks_connect_job.GetLoadState());
|
||
|
|
EXPECT_TRUE(socks_connect_job.HasEstablishedConnection());
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
|
||
|
|
sequenced_socket_data.Resume();
|
||
|
|
EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
|
||
|
|
EXPECT_TRUE(test_delegate.has_result());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check that TransportConnectJob's timeout is respected for the nested
|
||
|
|
// TransportConnectJob.
|
||
|
|
TEST_F(SOCKSConnectJobTest, TimeoutDuringDnsResolution) {
|
||
|
|
// Set HostResolver to hang.
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job.Connect();
|
||
|
|
|
||
|
|
// Just before the TransportConnectJob's timeout, nothing should have
|
||
|
|
// happened.
|
||
|
|
FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
|
||
|
|
EXPECT_TRUE(host_resolver_.has_pending_requests());
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
|
||
|
|
// Wait for exactly the TransportConnectJob's timeout to have passed. The Job
|
||
|
|
// should time out.
|
||
|
|
FastForwardBy(kTinyTime);
|
||
|
|
EXPECT_TRUE(test_delegate.has_result());
|
||
|
|
EXPECT_THAT(test_delegate.WaitForResult(),
|
||
|
|
test::IsError(ERR_PROXY_CONNECTION_FAILED));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check that SOCKSConnectJob's timeout is respected for the handshake phase.
|
||
|
|
TEST_F(SOCKSConnectJobTest, TimeoutDuringHandshake) {
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0),
|
||
|
|
};
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job.Connect();
|
||
|
|
|
||
|
|
// Just before the TransportConnectJob's timeout, nothing should have
|
||
|
|
// happened.
|
||
|
|
FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
EXPECT_TRUE(host_resolver_.has_pending_requests());
|
||
|
|
|
||
|
|
// DNS resolution completes, and the socket connects. The request should not
|
||
|
|
// time out, even after the TransportConnectJob's timeout passes. The
|
||
|
|
// SOCKSConnectJob's handshake timer should also be started.
|
||
|
|
host_resolver_.ResolveAllPending();
|
||
|
|
|
||
|
|
// Waiting until just before the SOCKS handshake times out. There should cause
|
||
|
|
// no observable change in the SOCKSConnectJob's status.
|
||
|
|
FastForwardBy(SOCKSConnectJob::HandshakeTimeoutForTesting() - kTinyTime);
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
|
||
|
|
// Wait for exactly the SOCKSConnectJob's handshake timeout has fully elapsed.
|
||
|
|
// The Job should time out.
|
||
|
|
FastForwardBy(kTinyTime);
|
||
|
|
EXPECT_FALSE(host_resolver_.has_pending_requests());
|
||
|
|
EXPECT_TRUE(test_delegate.has_result());
|
||
|
|
EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check initial priority is passed to the HostResolver, and priority can be
|
||
|
|
// modified.
|
||
|
|
TEST_F(SOCKSConnectJobTest, Priority) {
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
for (int initial_priority = MINIMUM_PRIORITY;
|
||
|
|
initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
|
||
|
|
for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
|
||
|
|
++new_priority) {
|
||
|
|
// Don't try changing priority to itself, as APIs may not allow that.
|
||
|
|
if (new_priority == initial_priority)
|
||
|
|
continue;
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(
|
||
|
|
static_cast<RequestPriority>(initial_priority), SocketTag(),
|
||
|
|
&common_connect_job_params_, CreateSOCKSParams(SOCKSVersion::V4),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
ASSERT_THAT(socks_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
|
||
|
|
ASSERT_TRUE(host_resolver_.has_pending_requests());
|
||
|
|
int request_id = host_resolver_.num_resolve();
|
||
|
|
EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
|
||
|
|
|
||
|
|
// Change priority.
|
||
|
|
socks_connect_job.ChangePriority(
|
||
|
|
static_cast<RequestPriority>(new_priority));
|
||
|
|
EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));
|
||
|
|
|
||
|
|
// Restore initial priority.
|
||
|
|
socks_connect_job.ChangePriority(
|
||
|
|
static_cast<RequestPriority>(initial_priority));
|
||
|
|
EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, SecureDnsPolicy) {
|
||
|
|
for (auto secure_dns_policy :
|
||
|
|
{SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(
|
||
|
|
DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V4, secure_dns_policy), &test_delegate,
|
||
|
|
nullptr /* net_log */);
|
||
|
|
ASSERT_THAT(socks_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
|
||
|
|
EXPECT_EQ(secure_dns_policy, host_resolver_.last_secure_dns_policy());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, ConnectTiming) {
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(ASYNC, ERR_IO_PENDING, 0),
|
||
|
|
MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength, 1),
|
||
|
|
MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 3),
|
||
|
|
};
|
||
|
|
|
||
|
|
MockRead reads[] = {
|
||
|
|
MockRead(SYNCHRONOUS, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength,
|
||
|
|
2),
|
||
|
|
MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 4),
|
||
|
|
};
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data(reads, writes);
|
||
|
|
// Host resolution is used to switch between sync and async connection
|
||
|
|
// behavior. The SOCKS layer can't distinguish between sync and async host
|
||
|
|
// resolution vs sync and async connection establishment, so just always
|
||
|
|
// make connection establishment synchroonous.
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
base::TimeTicks start = base::TimeTicks::Now();
|
||
|
|
socks_connect_job.Connect();
|
||
|
|
|
||
|
|
// DNS resolution completes after a short delay. The connection should be
|
||
|
|
// immediately established as well. The first write to the socket stalls.
|
||
|
|
FastForwardBy(kTinyTime);
|
||
|
|
host_resolver_.ResolveAllPending();
|
||
|
|
RunUntilIdle();
|
||
|
|
|
||
|
|
// After another short delay, data is received from the server.
|
||
|
|
FastForwardBy(kTinyTime);
|
||
|
|
sequenced_socket_data.Resume();
|
||
|
|
|
||
|
|
EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
|
||
|
|
// Proxy name resolution is not considered resolving the host name for
|
||
|
|
// ConnectionInfo. For SOCKS4, where the host name is also looked up via DNS,
|
||
|
|
// the resolution time is not currently reported.
|
||
|
|
EXPECT_EQ(base::TimeTicks(),
|
||
|
|
socks_connect_job.connect_timing().domain_lookup_start);
|
||
|
|
EXPECT_EQ(base::TimeTicks(),
|
||
|
|
socks_connect_job.connect_timing().domain_lookup_end);
|
||
|
|
|
||
|
|
// The "connect" time for socks proxies includes DNS resolution time.
|
||
|
|
EXPECT_EQ(start, socks_connect_job.connect_timing().connect_start);
|
||
|
|
EXPECT_EQ(start + 2 * kTinyTime,
|
||
|
|
socks_connect_job.connect_timing().connect_end);
|
||
|
|
|
||
|
|
// Since SSL was not negotiated, SSL times are null.
|
||
|
|
EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_start);
|
||
|
|
EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_end);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, CancelDuringDnsResolution) {
|
||
|
|
// Set HostResolver to hang.
|
||
|
|
host_resolver_.set_ondemand_mode(true);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
std::unique_ptr<SOCKSConnectJob> socks_connect_job =
|
||
|
|
std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job->Connect();
|
||
|
|
|
||
|
|
EXPECT_TRUE(host_resolver_.has_pending_requests());
|
||
|
|
|
||
|
|
socks_connect_job.reset();
|
||
|
|
RunUntilIdle();
|
||
|
|
EXPECT_FALSE(host_resolver_.has_pending_requests());
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, CancelDuringConnect) {
|
||
|
|
host_resolver_.set_synchronous_mode(true);
|
||
|
|
|
||
|
|
SequencedSocketData sequenced_socket_data{base::span<MockRead>(),
|
||
|
|
base::span<MockWrite>()};
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(ASYNC, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
std::unique_ptr<SOCKSConnectJob> socks_connect_job =
|
||
|
|
std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job->Connect();
|
||
|
|
// Host resolution should resolve immediately. The ConnectJob should currently
|
||
|
|
// be trying to connect.
|
||
|
|
EXPECT_FALSE(host_resolver_.has_pending_requests());
|
||
|
|
|
||
|
|
socks_connect_job.reset();
|
||
|
|
RunUntilIdle();
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
// Socket should have been destroyed.
|
||
|
|
EXPECT_FALSE(sequenced_socket_data.socket());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(SOCKSConnectJobTest, CancelDuringHandshake) {
|
||
|
|
host_resolver_.set_synchronous_mode(true);
|
||
|
|
|
||
|
|
// Hang at start of handshake.
|
||
|
|
MockWrite writes[] = {
|
||
|
|
MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0),
|
||
|
|
};
|
||
|
|
SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
|
||
|
|
sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
|
||
|
|
client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
|
||
|
|
|
||
|
|
TestConnectJobDelegate test_delegate;
|
||
|
|
std::unique_ptr<SOCKSConnectJob> socks_connect_job =
|
||
|
|
std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
|
||
|
|
&common_connect_job_params_,
|
||
|
|
CreateSOCKSParams(SOCKSVersion::V5),
|
||
|
|
&test_delegate, nullptr /* net_log */);
|
||
|
|
socks_connect_job->Connect();
|
||
|
|
// Host resolution should resolve immediately. The socket connecting, and the
|
||
|
|
// ConnectJob should currently be trying to send the SOCKS handshake.
|
||
|
|
EXPECT_FALSE(host_resolver_.has_pending_requests());
|
||
|
|
|
||
|
|
socks_connect_job.reset();
|
||
|
|
RunUntilIdle();
|
||
|
|
EXPECT_FALSE(test_delegate.has_result());
|
||
|
|
// Socket should have been destroyed.
|
||
|
|
EXPECT_FALSE(sequenced_socket_data.socket());
|
||
|
|
EXPECT_TRUE(sequenced_socket_data.AllWriteDataConsumed());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
} // namespace net
|