OLD | NEW |
| (Empty) |
1 // 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" | |
8 #include "net/base/host_resolver_unittest.h" | |
9 #include "net/base/listen_socket.h" | |
10 #include "net/base/test_completion_callback.h" | |
11 #include "net/base/winsock_init.h" | |
12 #include "net/socket/client_socket_factory.h" | |
13 #include "net/socket/tcp_client_socket.h" | |
14 #include "net/socket/socket_test_util.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "testing/platform_test.h" | |
17 | |
18 //----------------------------------------------------------------------------- | |
19 | |
20 namespace net { | |
21 | |
22 const char kSOCKSOkRequest[] = { 0x04, 0x01, 0x00, 0x50, 127, 0, 0, 1, 0 }; | |
23 const char kSOCKS4aInitialRequest[] = | |
24 { 0x04, 0x01, 0x00, 0x50, 0, 0, 0, 127, 0 }; | |
25 const char kSOCKSOkReply[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; | |
26 | |
27 class SOCKSClientSocketTest : public PlatformTest { | |
28 public: | |
29 SOCKSClientSocketTest(); | |
30 // Create a SOCKSClientSocket on top of a MockSocket. | |
31 SOCKSClientSocket* BuildMockSocket(MockRead reads[], MockWrite writes[], | |
32 const std::string& hostname, int port); | |
33 virtual void SetUp(); | |
34 | |
35 protected: | |
36 scoped_ptr<SOCKSClientSocket> user_sock_; | |
37 AddressList address_list_; | |
38 ClientSocket* tcp_sock_; | |
39 ScopedHostMapper host_mapper_; | |
40 TestCompletionCallback callback_; | |
41 scoped_refptr<RuleBasedHostMapper> mapper_; | |
42 HostResolver host_resolver_; | |
43 scoped_ptr<MockSocket> mock_socket_; | |
44 | |
45 private: | |
46 DISALLOW_COPY_AND_ASSIGN(SOCKSClientSocketTest); | |
47 }; | |
48 | |
49 SOCKSClientSocketTest::SOCKSClientSocketTest() | |
50 : host_resolver_(0, 0) { | |
51 } | |
52 | |
53 // Set up platform before every test case | |
54 void SOCKSClientSocketTest::SetUp() { | |
55 PlatformTest::SetUp(); | |
56 | |
57 // Resolve the "localhost" AddressList used by the tcp_connection to connect. | |
58 HostResolver resolver; | |
59 HostResolver::RequestInfo info("localhost", 1080); | |
60 int rv = resolver.Resolve(info, &address_list_, NULL, NULL); | |
61 ASSERT_EQ(OK, rv); | |
62 | |
63 // Create a new host mapping for the duration of this test case only. | |
64 mapper_ = new RuleBasedHostMapper(); | |
65 host_mapper_.Init(mapper_); | |
66 mapper_->AddRule("www.google.com", "127.0.0.1"); | |
67 } | |
68 | |
69 SOCKSClientSocket* SOCKSClientSocketTest::BuildMockSocket( | |
70 MockRead reads[], | |
71 MockWrite writes[], | |
72 const std::string& hostname, | |
73 int port) { | |
74 | |
75 TestCompletionCallback callback; | |
76 mock_socket_.reset(new StaticMockSocket(reads, writes)); | |
77 tcp_sock_ = new MockTCPClientSocket(address_list_, mock_socket_.get()); | |
78 | |
79 int rv = tcp_sock_->Connect(&callback); | |
80 EXPECT_EQ(ERR_IO_PENDING, rv); | |
81 rv = callback.WaitForResult(); | |
82 EXPECT_EQ(OK, rv); | |
83 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
84 | |
85 return new SOCKSClientSocket(tcp_sock_, | |
86 HostResolver::RequestInfo(hostname, port), | |
87 &host_resolver_); | |
88 } | |
89 | |
90 // Tests a complete handshake and the disconnection. | |
91 TEST_F(SOCKSClientSocketTest, CompleteHandshake) { | |
92 const std::string payload_write = "random data"; | |
93 const std::string payload_read = "moar random data"; | |
94 | |
95 MockWrite data_writes[] = { | |
96 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)), | |
97 MockWrite(true, payload_write.data(), payload_write.size()) }; | |
98 MockRead data_reads[] = { | |
99 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)), | |
100 MockRead(true, payload_read.data(), payload_read.size()) }; | |
101 | |
102 user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80)); | |
103 | |
104 // At this state the TCP connection is completed but not the SOCKS handshake. | |
105 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
106 EXPECT_FALSE(user_sock_->IsConnected()); | |
107 | |
108 int rv = user_sock_->Connect(&callback_); | |
109 EXPECT_EQ(ERR_IO_PENDING, rv); | |
110 EXPECT_FALSE(user_sock_->IsConnected()); | |
111 rv = callback_.WaitForResult(); | |
112 | |
113 EXPECT_EQ(OK, rv); | |
114 EXPECT_TRUE(user_sock_->IsConnected()); | |
115 EXPECT_EQ(SOCKSClientSocket::kSOCKS4, user_sock_->socks_version_); | |
116 | |
117 scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size()); | |
118 memcpy(buffer->data(), payload_write.data(), payload_write.size()); | |
119 rv = user_sock_->Write(buffer, payload_write.size(), &callback_); | |
120 EXPECT_EQ(ERR_IO_PENDING, rv); | |
121 rv = callback_.WaitForResult(); | |
122 EXPECT_EQ(static_cast<int>(payload_write.size()), rv); | |
123 | |
124 buffer = new IOBuffer(payload_read.size()); | |
125 rv = user_sock_->Read(buffer, payload_read.size(), &callback_); | |
126 EXPECT_EQ(ERR_IO_PENDING, rv); | |
127 rv = callback_.WaitForResult(); | |
128 EXPECT_EQ(static_cast<int>(payload_read.size()), rv); | |
129 EXPECT_EQ(payload_read, std::string(buffer->data(), payload_read.size())); | |
130 | |
131 user_sock_->Disconnect(); | |
132 EXPECT_FALSE(tcp_sock_->IsConnected()); | |
133 EXPECT_FALSE(user_sock_->IsConnected()); | |
134 } | |
135 | |
136 // List of responses from the socks server and the errors they should | |
137 // throw up are tested here. | |
138 TEST_F(SOCKSClientSocketTest, HandshakeFailures) { | |
139 const struct { | |
140 const char fail_reply[8]; | |
141 Error fail_code; | |
142 } tests[] = { | |
143 // Failure of the server response code | |
144 { | |
145 { 0x01, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }, | |
146 ERR_INVALID_RESPONSE, | |
147 }, | |
148 // Failure of the null byte | |
149 { | |
150 { 0x00, 0x5B, 0x00, 0x00, 0, 0, 0, 0 }, | |
151 ERR_FAILED, | |
152 }, | |
153 }; | |
154 | |
155 //--------------------------------------- | |
156 | |
157 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { | |
158 MockWrite data_writes[] = { | |
159 MockWrite(false, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
160 MockRead data_reads[] = { | |
161 MockRead(false, tests[i].fail_reply, arraysize(tests[i].fail_reply)) }; | |
162 | |
163 user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80)); | |
164 | |
165 int rv = user_sock_->Connect(&callback_); | |
166 EXPECT_EQ(ERR_IO_PENDING, rv); | |
167 rv = callback_.WaitForResult(); | |
168 EXPECT_EQ(tests[i].fail_code, rv); | |
169 EXPECT_FALSE(user_sock_->IsConnected()); | |
170 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
171 } | |
172 } | |
173 | |
174 // Tests scenario when the server sends the handshake response in | |
175 // more than one packet. | |
176 TEST_F(SOCKSClientSocketTest, PartialServerReads) { | |
177 const char kSOCKSPartialReply1[] = { 0x00 }; | |
178 const char kSOCKSPartialReply2[] = { 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; | |
179 | |
180 MockWrite data_writes[] = { | |
181 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
182 MockRead data_reads[] = { | |
183 MockRead(true, kSOCKSPartialReply1, arraysize(kSOCKSPartialReply1)), | |
184 MockRead(true, kSOCKSPartialReply2, arraysize(kSOCKSPartialReply2)) }; | |
185 | |
186 user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80)); | |
187 | |
188 int rv = user_sock_->Connect(&callback_); | |
189 EXPECT_EQ(ERR_IO_PENDING, rv); | |
190 rv = callback_.WaitForResult(); | |
191 EXPECT_EQ(OK, rv); | |
192 EXPECT_TRUE(user_sock_->IsConnected()); | |
193 } | |
194 | |
195 // Tests scenario when the client sends the handshake request in | |
196 // more than one packet. | |
197 TEST_F(SOCKSClientSocketTest, PartialClientWrites) { | |
198 const char kSOCKSPartialRequest1[] = { 0x04, 0x01 }; | |
199 const char kSOCKSPartialRequest2[] = { 0x00, 0x50, 127, 0, 0, 1, 0 }; | |
200 | |
201 MockWrite data_writes[] = { | |
202 MockWrite(true, arraysize(kSOCKSPartialRequest1)), | |
203 // simulate some empty writes | |
204 MockWrite(true, 0), | |
205 MockWrite(true, 0), | |
206 MockWrite(true, kSOCKSPartialRequest2, | |
207 arraysize(kSOCKSPartialRequest2)) }; | |
208 MockRead data_reads[] = { | |
209 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)) }; | |
210 | |
211 user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80)); | |
212 | |
213 int rv = user_sock_->Connect(&callback_); | |
214 EXPECT_EQ(ERR_IO_PENDING, rv); | |
215 rv = callback_.WaitForResult(); | |
216 EXPECT_EQ(OK, rv); | |
217 EXPECT_TRUE(user_sock_->IsConnected()); | |
218 } | |
219 | |
220 // Tests the case when the server sends a smaller sized handshake data | |
221 // and closes the connection. | |
222 TEST_F(SOCKSClientSocketTest, FailedSocketRead) { | |
223 MockWrite data_writes[] = { | |
224 MockWrite(true, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
225 MockRead data_reads[] = { | |
226 MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply) - 2), | |
227 // close connection unexpectedly | |
228 MockRead(false, 0) }; | |
229 | |
230 user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80)); | |
231 | |
232 int rv = user_sock_->Connect(&callback_); | |
233 EXPECT_EQ(ERR_IO_PENDING, rv); | |
234 rv = callback_.WaitForResult(); | |
235 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv); | |
236 EXPECT_FALSE(user_sock_->IsConnected()); | |
237 } | |
238 | |
239 // Tries to connect to an unknown DNS and on failure should revert to SOCKS4A. | |
240 TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) { | |
241 const char hostname[] = "unresolved.ipv4.address"; | |
242 | |
243 mapper_->AddSimulatedFailure(hostname); | |
244 | |
245 std::string request(kSOCKS4aInitialRequest, | |
246 arraysize(kSOCKS4aInitialRequest)); | |
247 request.append(hostname, arraysize(hostname)); | |
248 | |
249 MockWrite data_writes[] = { | |
250 MockWrite(false, request.data(), request.size()) }; | |
251 MockRead data_reads[] = { | |
252 MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) }; | |
253 | |
254 user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80)); | |
255 | |
256 int rv = user_sock_->Connect(&callback_); | |
257 EXPECT_EQ(ERR_IO_PENDING, rv); | |
258 rv = callback_.WaitForResult(); | |
259 EXPECT_EQ(OK, rv); | |
260 EXPECT_TRUE(user_sock_->IsConnected()); | |
261 EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_); | |
262 } | |
263 | |
264 // Tries to connect to a domain that resolves to IPv6. | |
265 // Should revert to SOCKS4a. | |
266 TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) { | |
267 const char hostname[] = "an.ipv6.address"; | |
268 | |
269 mapper_->AddRule(hostname, "2001:db8:8714:3a90::12"); | |
270 | |
271 std::string request(kSOCKS4aInitialRequest, | |
272 arraysize(kSOCKS4aInitialRequest)); | |
273 request.append(hostname, arraysize(hostname)); | |
274 | |
275 MockWrite data_writes[] = { | |
276 MockWrite(false, request.data(), request.size()) }; | |
277 MockRead data_reads[] = { | |
278 MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) }; | |
279 | |
280 user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80)); | |
281 | |
282 int rv = user_sock_->Connect(&callback_); | |
283 EXPECT_EQ(ERR_IO_PENDING, rv); | |
284 rv = callback_.WaitForResult(); | |
285 EXPECT_EQ(OK, rv); | |
286 EXPECT_TRUE(user_sock_->IsConnected()); | |
287 EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_); | |
288 } | |
289 | |
290 } // namespace net | |
291 | |
OLD | NEW |