OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/socket/socks5_client_socket.h" | |
6 | |
7 #include <algorithm> | |
8 #include <iterator> | |
9 #include <map> | |
10 | |
11 #include "base/sys_byteorder.h" | |
12 #include "net/base/address_list.h" | |
13 #include "net/base/net_log.h" | |
14 #include "net/base/net_log_unittest.h" | |
15 #include "net/base/test_completion_callback.h" | |
16 #include "net/base/winsock_init.h" | |
17 #include "net/dns/mock_host_resolver.h" | |
18 #include "net/socket/client_socket_factory.h" | |
19 #include "net/socket/socket_test_util.h" | |
20 #include "net/socket/tcp_client_socket.h" | |
21 #include "testing/gtest/include/gtest/gtest.h" | |
22 #include "testing/platform_test.h" | |
23 | |
24 //----------------------------------------------------------------------------- | |
25 | |
26 namespace net { | |
27 | |
28 namespace { | |
29 | |
30 // Base class to test SOCKS5ClientSocket | |
31 class SOCKS5ClientSocketTest : public PlatformTest { | |
32 public: | |
33 SOCKS5ClientSocketTest(); | |
34 // Create a SOCKSClientSocket on top of a MockSocket. | |
35 scoped_ptr<SOCKS5ClientSocket> BuildMockSocket(MockRead reads[], | |
36 size_t reads_count, | |
37 MockWrite writes[], | |
38 size_t writes_count, | |
39 const std::string& hostname, | |
40 int port, | |
41 NetLog* net_log); | |
42 | |
43 void SetUp() override; | |
44 | |
45 protected: | |
46 const uint16 kNwPort; | |
47 CapturingNetLog net_log_; | |
48 scoped_ptr<SOCKS5ClientSocket> user_sock_; | |
49 AddressList address_list_; | |
50 // Filled in by BuildMockSocket() and owned by its return value | |
51 // (which |user_sock| is set to). | |
52 StreamSocket* tcp_sock_; | |
53 TestCompletionCallback callback_; | |
54 scoped_ptr<MockHostResolver> host_resolver_; | |
55 scoped_ptr<SocketDataProvider> data_; | |
56 | |
57 private: | |
58 DISALLOW_COPY_AND_ASSIGN(SOCKS5ClientSocketTest); | |
59 }; | |
60 | |
61 SOCKS5ClientSocketTest::SOCKS5ClientSocketTest() | |
62 : kNwPort(base::HostToNet16(80)), | |
63 host_resolver_(new MockHostResolver) { | |
64 } | |
65 | |
66 // Set up platform before every test case | |
67 void SOCKS5ClientSocketTest::SetUp() { | |
68 PlatformTest::SetUp(); | |
69 | |
70 // Resolve the "localhost" AddressList used by the TCP connection to connect. | |
71 HostResolver::RequestInfo info(HostPortPair("www.socks-proxy.com", 1080)); | |
72 TestCompletionCallback callback; | |
73 int rv = host_resolver_->Resolve(info, | |
74 DEFAULT_PRIORITY, | |
75 &address_list_, | |
76 callback.callback(), | |
77 NULL, | |
78 BoundNetLog()); | |
79 ASSERT_EQ(ERR_IO_PENDING, rv); | |
80 rv = callback.WaitForResult(); | |
81 ASSERT_EQ(OK, rv); | |
82 } | |
83 | |
84 scoped_ptr<SOCKS5ClientSocket> SOCKS5ClientSocketTest::BuildMockSocket( | |
85 MockRead reads[], | |
86 size_t reads_count, | |
87 MockWrite writes[], | |
88 size_t writes_count, | |
89 const std::string& hostname, | |
90 int port, | |
91 NetLog* net_log) { | |
92 TestCompletionCallback callback; | |
93 data_.reset(new StaticSocketDataProvider(reads, reads_count, | |
94 writes, writes_count)); | |
95 tcp_sock_ = new MockTCPClientSocket(address_list_, net_log, data_.get()); | |
96 | |
97 int rv = tcp_sock_->Connect(callback.callback()); | |
98 EXPECT_EQ(ERR_IO_PENDING, rv); | |
99 rv = callback.WaitForResult(); | |
100 EXPECT_EQ(OK, rv); | |
101 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
102 | |
103 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); | |
104 // |connection| takes ownership of |tcp_sock_|, but keep a | |
105 // non-owning pointer to it. | |
106 connection->SetSocket(scoped_ptr<StreamSocket>(tcp_sock_)); | |
107 return scoped_ptr<SOCKS5ClientSocket>(new SOCKS5ClientSocket( | |
108 connection.Pass(), | |
109 HostResolver::RequestInfo(HostPortPair(hostname, port)))); | |
110 } | |
111 | |
112 // Tests a complete SOCKS5 handshake and the disconnection. | |
113 TEST_F(SOCKS5ClientSocketTest, CompleteHandshake) { | |
114 const std::string payload_write = "random data"; | |
115 const std::string payload_read = "moar random data"; | |
116 | |
117 const char kOkRequest[] = { | |
118 0x05, // Version | |
119 0x01, // Command (CONNECT) | |
120 0x00, // Reserved. | |
121 0x03, // Address type (DOMAINNAME). | |
122 0x09, // Length of domain (9) | |
123 // Domain string: | |
124 'l', 'o', 'c', 'a', 'l', 'h', 'o', 's', 't', | |
125 0x00, 0x50, // 16-bit port (80) | |
126 }; | |
127 | |
128 MockWrite data_writes[] = { | |
129 MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), | |
130 MockWrite(ASYNC, kOkRequest, arraysize(kOkRequest)), | |
131 MockWrite(ASYNC, payload_write.data(), payload_write.size()) }; | |
132 MockRead data_reads[] = { | |
133 MockRead(ASYNC, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), | |
134 MockRead(ASYNC, kSOCKS5OkResponse, kSOCKS5OkResponseLength), | |
135 MockRead(ASYNC, payload_read.data(), payload_read.size()) }; | |
136 | |
137 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
138 data_writes, arraysize(data_writes), | |
139 "localhost", 80, &net_log_); | |
140 | |
141 // At this state the TCP connection is completed but not the SOCKS handshake. | |
142 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
143 EXPECT_FALSE(user_sock_->IsConnected()); | |
144 | |
145 int rv = user_sock_->Connect(callback_.callback()); | |
146 EXPECT_EQ(ERR_IO_PENDING, rv); | |
147 EXPECT_FALSE(user_sock_->IsConnected()); | |
148 | |
149 CapturingNetLog::CapturedEntryList net_log_entries; | |
150 net_log_.GetEntries(&net_log_entries); | |
151 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries, 0, | |
152 NetLog::TYPE_SOCKS5_CONNECT)); | |
153 | |
154 rv = callback_.WaitForResult(); | |
155 | |
156 EXPECT_EQ(OK, rv); | |
157 EXPECT_TRUE(user_sock_->IsConnected()); | |
158 | |
159 net_log_.GetEntries(&net_log_entries); | |
160 EXPECT_TRUE(LogContainsEndEvent(net_log_entries, -1, | |
161 NetLog::TYPE_SOCKS5_CONNECT)); | |
162 | |
163 scoped_refptr<IOBuffer> buffer(new IOBuffer(payload_write.size())); | |
164 memcpy(buffer->data(), payload_write.data(), payload_write.size()); | |
165 rv = user_sock_->Write( | |
166 buffer.get(), payload_write.size(), callback_.callback()); | |
167 EXPECT_EQ(ERR_IO_PENDING, rv); | |
168 rv = callback_.WaitForResult(); | |
169 EXPECT_EQ(static_cast<int>(payload_write.size()), rv); | |
170 | |
171 buffer = new IOBuffer(payload_read.size()); | |
172 rv = | |
173 user_sock_->Read(buffer.get(), payload_read.size(), callback_.callback()); | |
174 EXPECT_EQ(ERR_IO_PENDING, rv); | |
175 rv = callback_.WaitForResult(); | |
176 EXPECT_EQ(static_cast<int>(payload_read.size()), rv); | |
177 EXPECT_EQ(payload_read, std::string(buffer->data(), payload_read.size())); | |
178 | |
179 user_sock_->Disconnect(); | |
180 EXPECT_FALSE(tcp_sock_->IsConnected()); | |
181 EXPECT_FALSE(user_sock_->IsConnected()); | |
182 } | |
183 | |
184 // Test that you can call Connect() again after having called Disconnect(). | |
185 TEST_F(SOCKS5ClientSocketTest, ConnectAndDisconnectTwice) { | |
186 const std::string hostname = "my-host-name"; | |
187 const char kSOCKS5DomainRequest[] = { | |
188 0x05, // VER | |
189 0x01, // CMD | |
190 0x00, // RSV | |
191 0x03, // ATYPE | |
192 }; | |
193 | |
194 std::string request(kSOCKS5DomainRequest, arraysize(kSOCKS5DomainRequest)); | |
195 request.push_back(hostname.size()); | |
196 request.append(hostname); | |
197 request.append(reinterpret_cast<const char*>(&kNwPort), sizeof(kNwPort)); | |
198 | |
199 for (int i = 0; i < 2; ++i) { | |
200 MockWrite data_writes[] = { | |
201 MockWrite(SYNCHRONOUS, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), | |
202 MockWrite(SYNCHRONOUS, request.data(), request.size()) | |
203 }; | |
204 MockRead data_reads[] = { | |
205 MockRead(SYNCHRONOUS, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), | |
206 MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength) | |
207 }; | |
208 | |
209 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
210 data_writes, arraysize(data_writes), | |
211 hostname, 80, NULL); | |
212 | |
213 int rv = user_sock_->Connect(callback_.callback()); | |
214 EXPECT_EQ(OK, rv); | |
215 EXPECT_TRUE(user_sock_->IsConnected()); | |
216 | |
217 user_sock_->Disconnect(); | |
218 EXPECT_FALSE(user_sock_->IsConnected()); | |
219 } | |
220 } | |
221 | |
222 // Test that we fail trying to connect to a hosname longer than 255 bytes. | |
223 TEST_F(SOCKS5ClientSocketTest, LargeHostNameFails) { | |
224 // Create a string of length 256, where each character is 'x'. | |
225 std::string large_host_name; | |
226 std::fill_n(std::back_inserter(large_host_name), 256, 'x'); | |
227 | |
228 // Create a SOCKS socket, with mock transport socket. | |
229 MockWrite data_writes[] = {MockWrite()}; | |
230 MockRead data_reads[] = {MockRead()}; | |
231 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
232 data_writes, arraysize(data_writes), | |
233 large_host_name, 80, NULL); | |
234 | |
235 // Try to connect -- should fail (without having read/written anything to | |
236 // the transport socket first) because the hostname is too long. | |
237 TestCompletionCallback callback; | |
238 int rv = user_sock_->Connect(callback.callback()); | |
239 EXPECT_EQ(ERR_SOCKS_CONNECTION_FAILED, rv); | |
240 } | |
241 | |
242 TEST_F(SOCKS5ClientSocketTest, PartialReadWrites) { | |
243 const std::string hostname = "www.google.com"; | |
244 | |
245 const char kOkRequest[] = { | |
246 0x05, // Version | |
247 0x01, // Command (CONNECT) | |
248 0x00, // Reserved. | |
249 0x03, // Address type (DOMAINNAME). | |
250 0x0E, // Length of domain (14) | |
251 // Domain string: | |
252 'w', 'w', 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm', | |
253 0x00, 0x50, // 16-bit port (80) | |
254 }; | |
255 | |
256 // Test for partial greet request write | |
257 { | |
258 const char partial1[] = { 0x05, 0x01 }; | |
259 const char partial2[] = { 0x00 }; | |
260 MockWrite data_writes[] = { | |
261 MockWrite(ASYNC, partial1, arraysize(partial1)), | |
262 MockWrite(ASYNC, partial2, arraysize(partial2)), | |
263 MockWrite(ASYNC, kOkRequest, arraysize(kOkRequest)) }; | |
264 MockRead data_reads[] = { | |
265 MockRead(ASYNC, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), | |
266 MockRead(ASYNC, kSOCKS5OkResponse, kSOCKS5OkResponseLength) }; | |
267 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
268 data_writes, arraysize(data_writes), | |
269 hostname, 80, &net_log_); | |
270 int rv = user_sock_->Connect(callback_.callback()); | |
271 EXPECT_EQ(ERR_IO_PENDING, rv); | |
272 | |
273 CapturingNetLog::CapturedEntryList net_log_entries; | |
274 net_log_.GetEntries(&net_log_entries); | |
275 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries, 0, | |
276 NetLog::TYPE_SOCKS5_CONNECT)); | |
277 | |
278 rv = callback_.WaitForResult(); | |
279 EXPECT_EQ(OK, rv); | |
280 EXPECT_TRUE(user_sock_->IsConnected()); | |
281 | |
282 net_log_.GetEntries(&net_log_entries); | |
283 EXPECT_TRUE(LogContainsEndEvent(net_log_entries, -1, | |
284 NetLog::TYPE_SOCKS5_CONNECT)); | |
285 } | |
286 | |
287 // Test for partial greet response read | |
288 { | |
289 const char partial1[] = { 0x05 }; | |
290 const char partial2[] = { 0x00 }; | |
291 MockWrite data_writes[] = { | |
292 MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), | |
293 MockWrite(ASYNC, kOkRequest, arraysize(kOkRequest)) }; | |
294 MockRead data_reads[] = { | |
295 MockRead(ASYNC, partial1, arraysize(partial1)), | |
296 MockRead(ASYNC, partial2, arraysize(partial2)), | |
297 MockRead(ASYNC, kSOCKS5OkResponse, kSOCKS5OkResponseLength) }; | |
298 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
299 data_writes, arraysize(data_writes), | |
300 hostname, 80, &net_log_); | |
301 int rv = user_sock_->Connect(callback_.callback()); | |
302 EXPECT_EQ(ERR_IO_PENDING, rv); | |
303 | |
304 CapturingNetLog::CapturedEntryList net_log_entries; | |
305 net_log_.GetEntries(&net_log_entries); | |
306 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries, 0, | |
307 NetLog::TYPE_SOCKS5_CONNECT)); | |
308 rv = callback_.WaitForResult(); | |
309 EXPECT_EQ(OK, rv); | |
310 EXPECT_TRUE(user_sock_->IsConnected()); | |
311 net_log_.GetEntries(&net_log_entries); | |
312 EXPECT_TRUE(LogContainsEndEvent(net_log_entries, -1, | |
313 NetLog::TYPE_SOCKS5_CONNECT)); | |
314 } | |
315 | |
316 // Test for partial handshake request write. | |
317 { | |
318 const int kSplitPoint = 3; // Break handshake write into two parts. | |
319 MockWrite data_writes[] = { | |
320 MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), | |
321 MockWrite(ASYNC, kOkRequest, kSplitPoint), | |
322 MockWrite(ASYNC, kOkRequest + kSplitPoint, | |
323 arraysize(kOkRequest) - kSplitPoint) | |
324 }; | |
325 MockRead data_reads[] = { | |
326 MockRead(ASYNC, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), | |
327 MockRead(ASYNC, kSOCKS5OkResponse, kSOCKS5OkResponseLength) }; | |
328 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
329 data_writes, arraysize(data_writes), | |
330 hostname, 80, &net_log_); | |
331 int rv = user_sock_->Connect(callback_.callback()); | |
332 EXPECT_EQ(ERR_IO_PENDING, rv); | |
333 CapturingNetLog::CapturedEntryList net_log_entries; | |
334 net_log_.GetEntries(&net_log_entries); | |
335 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries, 0, | |
336 NetLog::TYPE_SOCKS5_CONNECT)); | |
337 rv = callback_.WaitForResult(); | |
338 EXPECT_EQ(OK, rv); | |
339 EXPECT_TRUE(user_sock_->IsConnected()); | |
340 net_log_.GetEntries(&net_log_entries); | |
341 EXPECT_TRUE(LogContainsEndEvent(net_log_entries, -1, | |
342 NetLog::TYPE_SOCKS5_CONNECT)); | |
343 } | |
344 | |
345 // Test for partial handshake response read | |
346 { | |
347 const int kSplitPoint = 6; // Break the handshake read into two parts. | |
348 MockWrite data_writes[] = { | |
349 MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), | |
350 MockWrite(ASYNC, kOkRequest, arraysize(kOkRequest)) | |
351 }; | |
352 MockRead data_reads[] = { | |
353 MockRead(ASYNC, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), | |
354 MockRead(ASYNC, kSOCKS5OkResponse, kSplitPoint), | |
355 MockRead(ASYNC, kSOCKS5OkResponse + kSplitPoint, | |
356 kSOCKS5OkResponseLength - kSplitPoint) | |
357 }; | |
358 | |
359 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
360 data_writes, arraysize(data_writes), | |
361 hostname, 80, &net_log_); | |
362 int rv = user_sock_->Connect(callback_.callback()); | |
363 EXPECT_EQ(ERR_IO_PENDING, rv); | |
364 CapturingNetLog::CapturedEntryList net_log_entries; | |
365 net_log_.GetEntries(&net_log_entries); | |
366 EXPECT_TRUE(LogContainsBeginEvent(net_log_entries, 0, | |
367 NetLog::TYPE_SOCKS5_CONNECT)); | |
368 rv = callback_.WaitForResult(); | |
369 EXPECT_EQ(OK, rv); | |
370 EXPECT_TRUE(user_sock_->IsConnected()); | |
371 net_log_.GetEntries(&net_log_entries); | |
372 EXPECT_TRUE(LogContainsEndEvent(net_log_entries, -1, | |
373 NetLog::TYPE_SOCKS5_CONNECT)); | |
374 } | |
375 } | |
376 | |
377 } // namespace | |
378 | |
379 } // namespace net | |
OLD | NEW |