1 // Copyright (c) 2012 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.
5 #include "net/socket/socks5_client_socket.h"
11 #include "base/sys_byteorder.h"
12 #include "net/base/address_list.h"
13 #include "net/base/test_completion_callback.h"
14 #include "net/base/winsock_init.h"
15 #include "net/dns/mock_host_resolver.h"
16 #include "net/log/net_log.h"
17 #include "net/log/test_net_log.h"
18 #include "net/log/test_net_log_entry.h"
19 #include "net/log/test_net_log_util.h"
20 #include "net/socket/client_socket_factory.h"
21 #include "net/socket/socket_test_util.h"
22 #include "net/socket/tcp_client_socket.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "testing/platform_test.h"
26 //-----------------------------------------------------------------------------
32 // Base class to test SOCKS5ClientSocket
33 class SOCKS5ClientSocketTest
: public PlatformTest
{
35 SOCKS5ClientSocketTest();
36 // Create a SOCKSClientSocket on top of a MockSocket.
37 scoped_ptr
<SOCKS5ClientSocket
> BuildMockSocket(MockRead reads
[],
41 const std::string
& hostname
,
45 void SetUp() override
;
50 scoped_ptr
<SOCKS5ClientSocket
> user_sock_
;
51 AddressList address_list_
;
52 // Filled in by BuildMockSocket() and owned by its return value
53 // (which |user_sock| is set to).
54 StreamSocket
* tcp_sock_
;
55 TestCompletionCallback callback_
;
56 scoped_ptr
<MockHostResolver
> host_resolver_
;
57 scoped_ptr
<SocketDataProvider
> data_
;
60 DISALLOW_COPY_AND_ASSIGN(SOCKS5ClientSocketTest
);
63 SOCKS5ClientSocketTest::SOCKS5ClientSocketTest()
64 : kNwPort(base::HostToNet16(80)),
65 host_resolver_(new MockHostResolver
) {
68 // Set up platform before every test case
69 void SOCKS5ClientSocketTest::SetUp() {
70 PlatformTest::SetUp();
72 // Resolve the "localhost" AddressList used by the TCP connection to connect.
73 HostResolver::RequestInfo
info(HostPortPair("www.socks-proxy.com", 1080));
74 TestCompletionCallback callback
;
75 int rv
= host_resolver_
->Resolve(info
,
81 ASSERT_EQ(ERR_IO_PENDING
, rv
);
82 rv
= callback
.WaitForResult();
86 scoped_ptr
<SOCKS5ClientSocket
> SOCKS5ClientSocketTest::BuildMockSocket(
91 const std::string
& hostname
,
94 TestCompletionCallback callback
;
95 data_
.reset(new StaticSocketDataProvider(reads
, reads_count
,
96 writes
, writes_count
));
97 tcp_sock_
= new MockTCPClientSocket(address_list_
, net_log
, data_
.get());
99 int rv
= tcp_sock_
->Connect(callback
.callback());
100 EXPECT_EQ(ERR_IO_PENDING
, rv
);
101 rv
= callback
.WaitForResult();
103 EXPECT_TRUE(tcp_sock_
->IsConnected());
105 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
106 // |connection| takes ownership of |tcp_sock_|, but keep a
107 // non-owning pointer to it.
108 connection
->SetSocket(scoped_ptr
<StreamSocket
>(tcp_sock_
));
109 return scoped_ptr
<SOCKS5ClientSocket
>(new SOCKS5ClientSocket(
111 HostResolver::RequestInfo(HostPortPair(hostname
, port
))));
114 // Tests a complete SOCKS5 handshake and the disconnection.
115 TEST_F(SOCKS5ClientSocketTest
, CompleteHandshake
) {
116 const std::string payload_write
= "random data";
117 const std::string payload_read
= "moar random data";
119 const char kOkRequest
[] = {
121 0x01, // Command (CONNECT)
123 0x03, // Address type (DOMAINNAME).
124 0x09, // Length of domain (9)
126 'l', 'o', 'c', 'a', 'l', 'h', 'o', 's', 't',
127 0x00, 0x50, // 16-bit port (80)
130 MockWrite data_writes
[] = {
131 MockWrite(ASYNC
, kSOCKS5GreetRequest
, kSOCKS5GreetRequestLength
),
132 MockWrite(ASYNC
, kOkRequest
, arraysize(kOkRequest
)),
133 MockWrite(ASYNC
, payload_write
.data(), payload_write
.size()) };
134 MockRead data_reads
[] = {
135 MockRead(ASYNC
, kSOCKS5GreetResponse
, kSOCKS5GreetResponseLength
),
136 MockRead(ASYNC
, kSOCKS5OkResponse
, kSOCKS5OkResponseLength
),
137 MockRead(ASYNC
, payload_read
.data(), payload_read
.size()) };
139 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
140 data_writes
, arraysize(data_writes
),
141 "localhost", 80, &net_log_
);
143 // At this state the TCP connection is completed but not the SOCKS handshake.
144 EXPECT_TRUE(tcp_sock_
->IsConnected());
145 EXPECT_FALSE(user_sock_
->IsConnected());
147 int rv
= user_sock_
->Connect(callback_
.callback());
148 EXPECT_EQ(ERR_IO_PENDING
, rv
);
149 EXPECT_FALSE(user_sock_
->IsConnected());
151 TestNetLogEntry::List net_log_entries
;
152 net_log_
.GetEntries(&net_log_entries
);
153 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries
, 0,
154 NetLog::TYPE_SOCKS5_CONNECT
));
156 rv
= callback_
.WaitForResult();
159 EXPECT_TRUE(user_sock_
->IsConnected());
161 net_log_
.GetEntries(&net_log_entries
);
162 EXPECT_TRUE(LogContainsEndEvent(net_log_entries
, -1,
163 NetLog::TYPE_SOCKS5_CONNECT
));
165 scoped_refptr
<IOBuffer
> buffer(new IOBuffer(payload_write
.size()));
166 memcpy(buffer
->data(), payload_write
.data(), payload_write
.size());
167 rv
= user_sock_
->Write(
168 buffer
.get(), payload_write
.size(), callback_
.callback());
169 EXPECT_EQ(ERR_IO_PENDING
, rv
);
170 rv
= callback_
.WaitForResult();
171 EXPECT_EQ(static_cast<int>(payload_write
.size()), rv
);
173 buffer
= new IOBuffer(payload_read
.size());
175 user_sock_
->Read(buffer
.get(), payload_read
.size(), callback_
.callback());
176 EXPECT_EQ(ERR_IO_PENDING
, rv
);
177 rv
= callback_
.WaitForResult();
178 EXPECT_EQ(static_cast<int>(payload_read
.size()), rv
);
179 EXPECT_EQ(payload_read
, std::string(buffer
->data(), payload_read
.size()));
181 user_sock_
->Disconnect();
182 EXPECT_FALSE(tcp_sock_
->IsConnected());
183 EXPECT_FALSE(user_sock_
->IsConnected());
186 // Test that you can call Connect() again after having called Disconnect().
187 TEST_F(SOCKS5ClientSocketTest
, ConnectAndDisconnectTwice
) {
188 const std::string hostname
= "my-host-name";
189 const char kSOCKS5DomainRequest
[] = {
196 std::string
request(kSOCKS5DomainRequest
, arraysize(kSOCKS5DomainRequest
));
197 request
.push_back(hostname
.size());
198 request
.append(hostname
);
199 request
.append(reinterpret_cast<const char*>(&kNwPort
), sizeof(kNwPort
));
201 for (int i
= 0; i
< 2; ++i
) {
202 MockWrite data_writes
[] = {
203 MockWrite(SYNCHRONOUS
, kSOCKS5GreetRequest
, kSOCKS5GreetRequestLength
),
204 MockWrite(SYNCHRONOUS
, request
.data(), request
.size())
206 MockRead data_reads
[] = {
207 MockRead(SYNCHRONOUS
, kSOCKS5GreetResponse
, kSOCKS5GreetResponseLength
),
208 MockRead(SYNCHRONOUS
, kSOCKS5OkResponse
, kSOCKS5OkResponseLength
)
211 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
212 data_writes
, arraysize(data_writes
),
215 int rv
= user_sock_
->Connect(callback_
.callback());
217 EXPECT_TRUE(user_sock_
->IsConnected());
219 user_sock_
->Disconnect();
220 EXPECT_FALSE(user_sock_
->IsConnected());
224 // Test that we fail trying to connect to a hosname longer than 255 bytes.
225 TEST_F(SOCKS5ClientSocketTest
, LargeHostNameFails
) {
226 // Create a string of length 256, where each character is 'x'.
227 std::string large_host_name
;
228 std::fill_n(std::back_inserter(large_host_name
), 256, 'x');
230 // Create a SOCKS socket, with mock transport socket.
231 MockWrite data_writes
[] = {MockWrite()};
232 MockRead data_reads
[] = {MockRead()};
233 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
234 data_writes
, arraysize(data_writes
),
235 large_host_name
, 80, NULL
);
237 // Try to connect -- should fail (without having read/written anything to
238 // the transport socket first) because the hostname is too long.
239 TestCompletionCallback callback
;
240 int rv
= user_sock_
->Connect(callback
.callback());
241 EXPECT_EQ(ERR_SOCKS_CONNECTION_FAILED
, rv
);
244 TEST_F(SOCKS5ClientSocketTest
, PartialReadWrites
) {
245 const std::string hostname
= "www.google.com";
247 const char kOkRequest
[] = {
249 0x01, // Command (CONNECT)
251 0x03, // Address type (DOMAINNAME).
252 0x0E, // Length of domain (14)
254 'w', 'w', 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm',
255 0x00, 0x50, // 16-bit port (80)
258 // Test for partial greet request write
260 const char partial1
[] = { 0x05, 0x01 };
261 const char partial2
[] = { 0x00 };
262 MockWrite data_writes
[] = {
263 MockWrite(ASYNC
, partial1
, arraysize(partial1
)),
264 MockWrite(ASYNC
, partial2
, arraysize(partial2
)),
265 MockWrite(ASYNC
, kOkRequest
, arraysize(kOkRequest
)) };
266 MockRead data_reads
[] = {
267 MockRead(ASYNC
, kSOCKS5GreetResponse
, kSOCKS5GreetResponseLength
),
268 MockRead(ASYNC
, kSOCKS5OkResponse
, kSOCKS5OkResponseLength
) };
269 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
270 data_writes
, arraysize(data_writes
),
271 hostname
, 80, &net_log_
);
272 int rv
= user_sock_
->Connect(callback_
.callback());
273 EXPECT_EQ(ERR_IO_PENDING
, rv
);
275 TestNetLogEntry::List net_log_entries
;
276 net_log_
.GetEntries(&net_log_entries
);
277 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries
, 0,
278 NetLog::TYPE_SOCKS5_CONNECT
));
280 rv
= callback_
.WaitForResult();
282 EXPECT_TRUE(user_sock_
->IsConnected());
284 net_log_
.GetEntries(&net_log_entries
);
285 EXPECT_TRUE(LogContainsEndEvent(net_log_entries
, -1,
286 NetLog::TYPE_SOCKS5_CONNECT
));
289 // Test for partial greet response read
291 const char partial1
[] = { 0x05 };
292 const char partial2
[] = { 0x00 };
293 MockWrite data_writes
[] = {
294 MockWrite(ASYNC
, kSOCKS5GreetRequest
, kSOCKS5GreetRequestLength
),
295 MockWrite(ASYNC
, kOkRequest
, arraysize(kOkRequest
)) };
296 MockRead data_reads
[] = {
297 MockRead(ASYNC
, partial1
, arraysize(partial1
)),
298 MockRead(ASYNC
, partial2
, arraysize(partial2
)),
299 MockRead(ASYNC
, kSOCKS5OkResponse
, kSOCKS5OkResponseLength
) };
300 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
301 data_writes
, arraysize(data_writes
),
302 hostname
, 80, &net_log_
);
303 int rv
= user_sock_
->Connect(callback_
.callback());
304 EXPECT_EQ(ERR_IO_PENDING
, rv
);
306 TestNetLogEntry::List net_log_entries
;
307 net_log_
.GetEntries(&net_log_entries
);
308 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries
, 0,
309 NetLog::TYPE_SOCKS5_CONNECT
));
310 rv
= callback_
.WaitForResult();
312 EXPECT_TRUE(user_sock_
->IsConnected());
313 net_log_
.GetEntries(&net_log_entries
);
314 EXPECT_TRUE(LogContainsEndEvent(net_log_entries
, -1,
315 NetLog::TYPE_SOCKS5_CONNECT
));
318 // Test for partial handshake request write.
320 const int kSplitPoint
= 3; // Break handshake write into two parts.
321 MockWrite data_writes
[] = {
322 MockWrite(ASYNC
, kSOCKS5GreetRequest
, kSOCKS5GreetRequestLength
),
323 MockWrite(ASYNC
, kOkRequest
, kSplitPoint
),
324 MockWrite(ASYNC
, kOkRequest
+ kSplitPoint
,
325 arraysize(kOkRequest
) - kSplitPoint
)
327 MockRead data_reads
[] = {
328 MockRead(ASYNC
, kSOCKS5GreetResponse
, kSOCKS5GreetResponseLength
),
329 MockRead(ASYNC
, kSOCKS5OkResponse
, kSOCKS5OkResponseLength
) };
330 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
331 data_writes
, arraysize(data_writes
),
332 hostname
, 80, &net_log_
);
333 int rv
= user_sock_
->Connect(callback_
.callback());
334 EXPECT_EQ(ERR_IO_PENDING
, rv
);
335 TestNetLogEntry::List net_log_entries
;
336 net_log_
.GetEntries(&net_log_entries
);
337 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries
, 0,
338 NetLog::TYPE_SOCKS5_CONNECT
));
339 rv
= callback_
.WaitForResult();
341 EXPECT_TRUE(user_sock_
->IsConnected());
342 net_log_
.GetEntries(&net_log_entries
);
343 EXPECT_TRUE(LogContainsEndEvent(net_log_entries
, -1,
344 NetLog::TYPE_SOCKS5_CONNECT
));
347 // Test for partial handshake response read
349 const int kSplitPoint
= 6; // Break the handshake read into two parts.
350 MockWrite data_writes
[] = {
351 MockWrite(ASYNC
, kSOCKS5GreetRequest
, kSOCKS5GreetRequestLength
),
352 MockWrite(ASYNC
, kOkRequest
, arraysize(kOkRequest
))
354 MockRead data_reads
[] = {
355 MockRead(ASYNC
, kSOCKS5GreetResponse
, kSOCKS5GreetResponseLength
),
356 MockRead(ASYNC
, kSOCKS5OkResponse
, kSplitPoint
),
357 MockRead(ASYNC
, kSOCKS5OkResponse
+ kSplitPoint
,
358 kSOCKS5OkResponseLength
- kSplitPoint
)
361 user_sock_
= BuildMockSocket(data_reads
, arraysize(data_reads
),
362 data_writes
, arraysize(data_writes
),
363 hostname
, 80, &net_log_
);
364 int rv
= user_sock_
->Connect(callback_
.callback());
365 EXPECT_EQ(ERR_IO_PENDING
, rv
);
366 TestNetLogEntry::List net_log_entries
;
367 net_log_
.GetEntries(&net_log_entries
);
368 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries
, 0,
369 NetLog::TYPE_SOCKS5_CONNECT
));
370 rv
= callback_
.WaitForResult();
372 EXPECT_TRUE(user_sock_
->IsConnected());
373 net_log_
.GetEntries(&net_log_entries
);
374 EXPECT_TRUE(LogContainsEndEvent(net_log_entries
, -1,
375 NetLog::TYPE_SOCKS5_CONNECT
));