blob: 6cf450e931dc381e07892930224e6c7de5ff1b2b [file] [log] [blame]
[email protected]76a51ac82009-06-28 07:58:581// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/socket/socks_client_socket.h"
6
7#include "net/base/address_list.h"
[email protected]5a05c47a2009-11-02 23:25:198#include "net/base/load_log.h"
9#include "net/base/load_log_unittest.h"
[email protected]b59ff372009-07-15 22:04:3210#include "net/base/mock_host_resolver.h"
[email protected]76a51ac82009-06-28 07:58:5811#include "net/base/test_completion_callback.h"
12#include "net/base/winsock_init.h"
13#include "net/socket/client_socket_factory.h"
14#include "net/socket/tcp_client_socket.h"
15#include "net/socket/socket_test_util.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "testing/platform_test.h"
18
19//-----------------------------------------------------------------------------
20
21namespace net {
22
23const char kSOCKSOkRequest[] = { 0x04, 0x01, 0x00, 0x50, 127, 0, 0, 1, 0 };
24const char kSOCKS4aInitialRequest[] =
25 { 0x04, 0x01, 0x00, 0x50, 0, 0, 0, 127, 0 };
26const char kSOCKSOkReply[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 };
27
28class SOCKSClientSocketTest : public PlatformTest {
29 public:
30 SOCKSClientSocketTest();
31 // Create a SOCKSClientSocket on top of a MockSocket.
32 SOCKSClientSocket* BuildMockSocket(MockRead reads[], MockWrite writes[],
[email protected]16a02742010-01-07 22:50:1033 HostResolver* host_resolver,
[email protected]76a51ac82009-06-28 07:58:5834 const std::string& hostname, int port);
35 virtual void SetUp();
36
37 protected:
38 scoped_ptr<SOCKSClientSocket> user_sock_;
39 AddressList address_list_;
40 ClientSocket* tcp_sock_;
[email protected]76a51ac82009-06-28 07:58:5841 TestCompletionCallback callback_;
[email protected]b59ff372009-07-15 22:04:3242 scoped_refptr<MockHostResolver> host_resolver_;
[email protected]5ecc992a42009-11-11 01:41:5943 scoped_ptr<SocketDataProvider> data_;
[email protected]76a51ac82009-06-28 07:58:5844
45 private:
46 DISALLOW_COPY_AND_ASSIGN(SOCKSClientSocketTest);
47};
48
49SOCKSClientSocketTest::SOCKSClientSocketTest()
[email protected]b59ff372009-07-15 22:04:3250 : host_resolver_(new MockHostResolver) {
[email protected]76a51ac82009-06-28 07:58:5851}
52
53// Set up platform before every test case
54void SOCKSClientSocketTest::SetUp() {
55 PlatformTest::SetUp();
[email protected]76a51ac82009-06-28 07:58:5856}
57
58SOCKSClientSocket* SOCKSClientSocketTest::BuildMockSocket(
59 MockRead reads[],
60 MockWrite writes[],
[email protected]16a02742010-01-07 22:50:1061 HostResolver* host_resolver,
[email protected]76a51ac82009-06-28 07:58:5862 const std::string& hostname,
63 int port) {
64
65 TestCompletionCallback callback;
[email protected]5ecc992a42009-11-11 01:41:5966 data_.reset(new StaticSocketDataProvider(reads, writes));
67 tcp_sock_ = new MockTCPClientSocket(address_list_, data_.get());
[email protected]76a51ac82009-06-28 07:58:5868
[email protected]5a05c47a2009-11-02 23:25:1969 int rv = tcp_sock_->Connect(&callback, NULL);
[email protected]76a51ac82009-06-28 07:58:5870 EXPECT_EQ(ERR_IO_PENDING, rv);
71 rv = callback.WaitForResult();
72 EXPECT_EQ(OK, rv);
73 EXPECT_TRUE(tcp_sock_->IsConnected());
74
75 return new SOCKSClientSocket(tcp_sock_,
76 HostResolver::RequestInfo(hostname, port),
[email protected]16a02742010-01-07 22:50:1077 host_resolver);
[email protected]76a51ac82009-06-28 07:58:5878}
79
[email protected]16a02742010-01-07 22:50:1080// Implementation of HostResolver that never completes its resolve request.
81// We use this in the test "DisconnectWhileHostResolveInProgress" to make
82// sure that the outstanding resolve request gets cancelled.
83class HangingHostResolver : public HostResolver {
84 public:
85 HangingHostResolver() : outstanding_request_(NULL) {}
86
87 virtual int Resolve(const RequestInfo& info,
88 AddressList* addresses,
89 CompletionCallback* callback,
90 RequestHandle* out_req,
91 LoadLog* load_log) {
92 EXPECT_FALSE(HasOutstandingRequest());
93 outstanding_request_ = reinterpret_cast<RequestHandle>(1);
94 *out_req = outstanding_request_;
95 return ERR_IO_PENDING;
96 }
97
98 virtual void CancelRequest(RequestHandle req) {
99 EXPECT_TRUE(HasOutstandingRequest());
100 EXPECT_EQ(outstanding_request_, req);
101 outstanding_request_ = NULL;
102 }
103
104 virtual void AddObserver(Observer* observer) {}
105 virtual void RemoveObserver(Observer* observer) {}
106 virtual HostCache* GetHostCache() { return NULL; }
107 virtual void Shutdown() {}
108
109 bool HasOutstandingRequest() {
110 return outstanding_request_ != NULL;
111 }
112
113 private:
114 RequestHandle outstanding_request_;
115
116 DISALLOW_COPY_AND_ASSIGN(HangingHostResolver);
117};
118
[email protected]76a51ac82009-06-28 07:58:58119// Tests a complete handshake and the disconnection.
120TEST_F(SOCKSClientSocketTest, CompleteHandshake) {
121 const std::string payload_write = "random data";
122 const std::string payload_read = "moar random data";
123
124 MockWrite data_writes[] = {
125 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)),
126 MockWrite(true, payload_write.data(), payload_write.size()) };
127 MockRead data_reads[] = {
128 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)),
129 MockRead(true, payload_read.data(), payload_read.size()) };
130
[email protected]16a02742010-01-07 22:50:10131 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
132 "localhost", 80));
[email protected]76a51ac82009-06-28 07:58:58133
134 // At this state the TCP connection is completed but not the SOCKS handshake.
135 EXPECT_TRUE(tcp_sock_->IsConnected());
136 EXPECT_FALSE(user_sock_->IsConnected());
137
[email protected]60c4c412009-11-06 19:59:36138 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]5a05c47a2009-11-02 23:25:19139 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58140 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19141 EXPECT_TRUE(
142 LogContains(*log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58143 EXPECT_FALSE(user_sock_->IsConnected());
144 rv = callback_.WaitForResult();
145
146 EXPECT_EQ(OK, rv);
147 EXPECT_TRUE(user_sock_->IsConnected());
148 EXPECT_EQ(SOCKSClientSocket::kSOCKS4, user_sock_->socks_version_);
[email protected]5a05c47a2009-11-02 23:25:19149 EXPECT_TRUE(LogContains(
150 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58151
152 scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size());
153 memcpy(buffer->data(), payload_write.data(), payload_write.size());
154 rv = user_sock_->Write(buffer, payload_write.size(), &callback_);
155 EXPECT_EQ(ERR_IO_PENDING, rv);
156 rv = callback_.WaitForResult();
157 EXPECT_EQ(static_cast<int>(payload_write.size()), rv);
158
159 buffer = new IOBuffer(payload_read.size());
160 rv = user_sock_->Read(buffer, payload_read.size(), &callback_);
161 EXPECT_EQ(ERR_IO_PENDING, rv);
162 rv = callback_.WaitForResult();
163 EXPECT_EQ(static_cast<int>(payload_read.size()), rv);
164 EXPECT_EQ(payload_read, std::string(buffer->data(), payload_read.size()));
165
166 user_sock_->Disconnect();
167 EXPECT_FALSE(tcp_sock_->IsConnected());
168 EXPECT_FALSE(user_sock_->IsConnected());
169}
170
171// List of responses from the socks server and the errors they should
172// throw up are tested here.
173TEST_F(SOCKSClientSocketTest, HandshakeFailures) {
174 const struct {
175 const char fail_reply[8];
176 Error fail_code;
177 } tests[] = {
178 // Failure of the server response code
179 {
180 { 0x01, 0x5A, 0x00, 0x00, 0, 0, 0, 0 },
181 ERR_INVALID_RESPONSE,
182 },
183 // Failure of the null byte
184 {
185 { 0x00, 0x5B, 0x00, 0x00, 0, 0, 0, 0 },
186 ERR_FAILED,
187 },
188 };
189
190 //---------------------------------------
191
192 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
193 MockWrite data_writes[] = {
194 MockWrite(false, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) };
195 MockRead data_reads[] = {
196 MockRead(false, tests[i].fail_reply, arraysize(tests[i].fail_reply)) };
197
[email protected]16a02742010-01-07 22:50:10198 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
199 "localhost", 80));
[email protected]60c4c412009-11-06 19:59:36200 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58201
[email protected]5a05c47a2009-11-02 23:25:19202 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58203 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19204 EXPECT_TRUE(LogContains(
205 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58206 rv = callback_.WaitForResult();
207 EXPECT_EQ(tests[i].fail_code, rv);
208 EXPECT_FALSE(user_sock_->IsConnected());
209 EXPECT_TRUE(tcp_sock_->IsConnected());
[email protected]5a05c47a2009-11-02 23:25:19210 EXPECT_TRUE(LogContains(
211 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58212 }
213}
214
215// Tests scenario when the server sends the handshake response in
216// more than one packet.
217TEST_F(SOCKSClientSocketTest, PartialServerReads) {
218 const char kSOCKSPartialReply1[] = { 0x00 };
219 const char kSOCKSPartialReply2[] = { 0x5A, 0x00, 0x00, 0, 0, 0, 0 };
220
221 MockWrite data_writes[] = {
222 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) };
223 MockRead data_reads[] = {
224 MockRead(true, kSOCKSPartialReply1, arraysize(kSOCKSPartialReply1)),
225 MockRead(true, kSOCKSPartialReply2, arraysize(kSOCKSPartialReply2)) };
226
[email protected]16a02742010-01-07 22:50:10227 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
228 "localhost", 80));
[email protected]60c4c412009-11-06 19:59:36229 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58230
[email protected]5a05c47a2009-11-02 23:25:19231 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58232 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19233 EXPECT_TRUE(LogContains(
234 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58235 rv = callback_.WaitForResult();
236 EXPECT_EQ(OK, rv);
237 EXPECT_TRUE(user_sock_->IsConnected());
[email protected]5a05c47a2009-11-02 23:25:19238 EXPECT_TRUE(LogContains(
239 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58240}
241
242// Tests scenario when the client sends the handshake request in
243// more than one packet.
244TEST_F(SOCKSClientSocketTest, PartialClientWrites) {
245 const char kSOCKSPartialRequest1[] = { 0x04, 0x01 };
246 const char kSOCKSPartialRequest2[] = { 0x00, 0x50, 127, 0, 0, 1, 0 };
247
248 MockWrite data_writes[] = {
249 MockWrite(true, arraysize(kSOCKSPartialRequest1)),
250 // simulate some empty writes
251 MockWrite(true, 0),
252 MockWrite(true, 0),
253 MockWrite(true, kSOCKSPartialRequest2,
254 arraysize(kSOCKSPartialRequest2)) };
255 MockRead data_reads[] = {
256 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
257
[email protected]16a02742010-01-07 22:50:10258 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
259 "localhost", 80));
[email protected]60c4c412009-11-06 19:59:36260 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58261
[email protected]5a05c47a2009-11-02 23:25:19262 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58263 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19264 EXPECT_TRUE(LogContains(
265 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58266 rv = callback_.WaitForResult();
267 EXPECT_EQ(OK, rv);
268 EXPECT_TRUE(user_sock_->IsConnected());
[email protected]5a05c47a2009-11-02 23:25:19269 EXPECT_TRUE(LogContains(
270 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58271}
272
273// Tests the case when the server sends a smaller sized handshake data
274// and closes the connection.
275TEST_F(SOCKSClientSocketTest, FailedSocketRead) {
276 MockWrite data_writes[] = {
277 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) };
278 MockRead data_reads[] = {
279 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply) - 2),
280 // close connection unexpectedly
281 MockRead(false, 0) };
282
[email protected]16a02742010-01-07 22:50:10283 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
284 "localhost", 80));
[email protected]60c4c412009-11-06 19:59:36285 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58286
[email protected]5a05c47a2009-11-02 23:25:19287 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58288 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19289 EXPECT_TRUE(LogContains(
290 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58291 rv = callback_.WaitForResult();
292 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
293 EXPECT_FALSE(user_sock_->IsConnected());
[email protected]5a05c47a2009-11-02 23:25:19294 EXPECT_TRUE(LogContains(
295 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58296}
297
298// Tries to connect to an unknown DNS and on failure should revert to SOCKS4A.
299TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) {
300 const char hostname[] = "unresolved.ipv4.address";
301
[email protected]b59ff372009-07-15 22:04:32302 host_resolver_->rules()->AddSimulatedFailure(hostname);
[email protected]76a51ac82009-06-28 07:58:58303
304 std::string request(kSOCKS4aInitialRequest,
305 arraysize(kSOCKS4aInitialRequest));
306 request.append(hostname, arraysize(hostname));
307
308 MockWrite data_writes[] = {
309 MockWrite(false, request.data(), request.size()) };
310 MockRead data_reads[] = {
311 MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
312
[email protected]16a02742010-01-07 22:50:10313 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
314 hostname, 80));
[email protected]60c4c412009-11-06 19:59:36315 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58316
[email protected]5a05c47a2009-11-02 23:25:19317 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58318 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19319 EXPECT_TRUE(LogContains(
320 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58321 rv = callback_.WaitForResult();
322 EXPECT_EQ(OK, rv);
323 EXPECT_TRUE(user_sock_->IsConnected());
324 EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_);
[email protected]5a05c47a2009-11-02 23:25:19325 EXPECT_TRUE(LogContains(
326 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58327}
328
329// Tries to connect to a domain that resolves to IPv6.
330// Should revert to SOCKS4a.
331TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) {
332 const char hostname[] = "an.ipv6.address";
333
[email protected]40f15352009-07-18 08:02:08334 host_resolver_->rules()->AddIPv6Rule(hostname, "2001:db8:8714:3a90::12");
[email protected]76a51ac82009-06-28 07:58:58335
336 std::string request(kSOCKS4aInitialRequest,
337 arraysize(kSOCKS4aInitialRequest));
338 request.append(hostname, arraysize(hostname));
339
340 MockWrite data_writes[] = {
341 MockWrite(false, request.data(), request.size()) };
342 MockRead data_reads[] = {
343 MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
344
[email protected]16a02742010-01-07 22:50:10345 user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
346 hostname, 80));
[email protected]60c4c412009-11-06 19:59:36347 scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
[email protected]76a51ac82009-06-28 07:58:58348
[email protected]5a05c47a2009-11-02 23:25:19349 int rv = user_sock_->Connect(&callback_, log);
[email protected]76a51ac82009-06-28 07:58:58350 EXPECT_EQ(ERR_IO_PENDING, rv);
[email protected]5a05c47a2009-11-02 23:25:19351 EXPECT_TRUE(LogContains(
352 *log, 0, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_BEGIN));
[email protected]76a51ac82009-06-28 07:58:58353 rv = callback_.WaitForResult();
354 EXPECT_EQ(OK, rv);
355 EXPECT_TRUE(user_sock_->IsConnected());
356 EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_);
[email protected]5a05c47a2009-11-02 23:25:19357 EXPECT_TRUE(LogContains(
358 *log, -1, LoadLog::TYPE_SOCKS_CONNECT, LoadLog::PHASE_END));
[email protected]76a51ac82009-06-28 07:58:58359}
360
[email protected]16a02742010-01-07 22:50:10361// Calls Disconnect() while a host resolve is in progress. The outstanding host
362// resolve should be cancelled.
363TEST_F(SOCKSClientSocketTest, DisconnectWhileHostResolveInProgress) {
364 scoped_refptr<HangingHostResolver> hanging_resolver =
365 new HangingHostResolver();
366
367 // Doesn't matter what the socket data is, we will never use it -- garbage.
368 MockWrite data_writes[] = { MockWrite(false, "", 0) };
369 MockRead data_reads[] = { MockRead(false, "", 0) };
370
371 user_sock_.reset(BuildMockSocket(data_reads, data_writes, hanging_resolver,
372 "foo", 80));
373
374 // Start connecting (will get stuck waiting for the host to resolve).
375 int rv = user_sock_->Connect(&callback_, NULL);
376 EXPECT_EQ(ERR_IO_PENDING, rv);
377
378 EXPECT_FALSE(user_sock_->IsConnected());
379 EXPECT_FALSE(user_sock_->IsConnectedAndIdle());
380
381 // The host resolver should have received the resolve request.
382 EXPECT_TRUE(hanging_resolver->HasOutstandingRequest());
383
384 // Disconnect the SOCKS socket -- this should cancel the outstanding resolve.
385 user_sock_->Disconnect();
386
387 EXPECT_FALSE(hanging_resolver->HasOutstandingRequest());
388
389 EXPECT_FALSE(user_sock_->IsConnected());
390 EXPECT_FALSE(user_sock_->IsConnectedAndIdle());
391}
392
[email protected]76a51ac82009-06-28 07:58:58393} // namespace net