| 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/socks_client_socket.h" | |
| 6 | |
| 7 #include "base/memory/scoped_ptr.h" | |
| 8 #include "net/base/address_list.h" | |
| 9 #include "net/base/net_log.h" | |
| 10 #include "net/base/net_log_unittest.h" | |
| 11 #include "net/base/test_completion_callback.h" | |
| 12 #include "net/base/winsock_init.h" | |
| 13 #include "net/dns/host_resolver.h" | |
| 14 #include "net/dns/mock_host_resolver.h" | |
| 15 #include "net/socket/client_socket_factory.h" | |
| 16 #include "net/socket/socket_test_util.h" | |
| 17 #include "net/socket/tcp_client_socket.h" | |
| 18 #include "testing/gtest/include/gtest/gtest.h" | |
| 19 #include "testing/platform_test.h" | |
| 20 | |
| 21 //----------------------------------------------------------------------------- | |
| 22 | |
| 23 namespace net { | |
| 24 | |
| 25 const char kSOCKSOkRequest[] = { 0x04, 0x01, 0x00, 0x50, 127, 0, 0, 1, 0 }; | |
| 26 const char kSOCKSOkReply[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; | |
| 27 | |
| 28 class SOCKSClientSocketTest : public PlatformTest { | |
| 29 public: | |
| 30 SOCKSClientSocketTest(); | |
| 31 // Create a SOCKSClientSocket on top of a MockSocket. | |
| 32 scoped_ptr<SOCKSClientSocket> BuildMockSocket( | |
| 33 MockRead reads[], size_t reads_count, | |
| 34 MockWrite writes[], size_t writes_count, | |
| 35 HostResolver* host_resolver, | |
| 36 const std::string& hostname, int port, | |
| 37 NetLog* net_log); | |
| 38 void SetUp() override; | |
| 39 | |
| 40 protected: | |
| 41 scoped_ptr<SOCKSClientSocket> user_sock_; | |
| 42 AddressList address_list_; | |
| 43 // Filled in by BuildMockSocket() and owned by its return value | |
| 44 // (which |user_sock| is set to). | |
| 45 StreamSocket* tcp_sock_; | |
| 46 TestCompletionCallback callback_; | |
| 47 scoped_ptr<MockHostResolver> host_resolver_; | |
| 48 scoped_ptr<SocketDataProvider> data_; | |
| 49 }; | |
| 50 | |
| 51 SOCKSClientSocketTest::SOCKSClientSocketTest() | |
| 52 : host_resolver_(new MockHostResolver) { | |
| 53 } | |
| 54 | |
| 55 // Set up platform before every test case | |
| 56 void SOCKSClientSocketTest::SetUp() { | |
| 57 PlatformTest::SetUp(); | |
| 58 } | |
| 59 | |
| 60 scoped_ptr<SOCKSClientSocket> SOCKSClientSocketTest::BuildMockSocket( | |
| 61 MockRead reads[], | |
| 62 size_t reads_count, | |
| 63 MockWrite writes[], | |
| 64 size_t writes_count, | |
| 65 HostResolver* host_resolver, | |
| 66 const std::string& hostname, | |
| 67 int port, | |
| 68 NetLog* net_log) { | |
| 69 | |
| 70 TestCompletionCallback callback; | |
| 71 data_.reset(new StaticSocketDataProvider(reads, reads_count, | |
| 72 writes, writes_count)); | |
| 73 tcp_sock_ = new MockTCPClientSocket(address_list_, net_log, data_.get()); | |
| 74 | |
| 75 int rv = tcp_sock_->Connect(callback.callback()); | |
| 76 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 77 rv = callback.WaitForResult(); | |
| 78 EXPECT_EQ(OK, rv); | |
| 79 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
| 80 | |
| 81 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); | |
| 82 // |connection| takes ownership of |tcp_sock_|, but keep a | |
| 83 // non-owning pointer to it. | |
| 84 connection->SetSocket(scoped_ptr<StreamSocket>(tcp_sock_)); | |
| 85 return scoped_ptr<SOCKSClientSocket>(new SOCKSClientSocket( | |
| 86 connection.Pass(), | |
| 87 HostResolver::RequestInfo(HostPortPair(hostname, port)), | |
| 88 DEFAULT_PRIORITY, | |
| 89 host_resolver)); | |
| 90 } | |
| 91 | |
| 92 // Implementation of HostResolver that never completes its resolve request. | |
| 93 // We use this in the test "DisconnectWhileHostResolveInProgress" to make | |
| 94 // sure that the outstanding resolve request gets cancelled. | |
| 95 class HangingHostResolverWithCancel : public HostResolver { | |
| 96 public: | |
| 97 HangingHostResolverWithCancel() : outstanding_request_(NULL) {} | |
| 98 | |
| 99 int Resolve(const RequestInfo& info, | |
| 100 RequestPriority priority, | |
| 101 AddressList* addresses, | |
| 102 const CompletionCallback& callback, | |
| 103 RequestHandle* out_req, | |
| 104 const BoundNetLog& net_log) override { | |
| 105 DCHECK(addresses); | |
| 106 DCHECK_EQ(false, callback.is_null()); | |
| 107 EXPECT_FALSE(HasOutstandingRequest()); | |
| 108 outstanding_request_ = reinterpret_cast<RequestHandle>(1); | |
| 109 *out_req = outstanding_request_; | |
| 110 return ERR_IO_PENDING; | |
| 111 } | |
| 112 | |
| 113 int ResolveFromCache(const RequestInfo& info, | |
| 114 AddressList* addresses, | |
| 115 const BoundNetLog& net_log) override { | |
| 116 NOTIMPLEMENTED(); | |
| 117 return ERR_UNEXPECTED; | |
| 118 } | |
| 119 | |
| 120 void CancelRequest(RequestHandle req) override { | |
| 121 EXPECT_TRUE(HasOutstandingRequest()); | |
| 122 EXPECT_EQ(outstanding_request_, req); | |
| 123 outstanding_request_ = NULL; | |
| 124 } | |
| 125 | |
| 126 bool HasOutstandingRequest() { | |
| 127 return outstanding_request_ != NULL; | |
| 128 } | |
| 129 | |
| 130 private: | |
| 131 RequestHandle outstanding_request_; | |
| 132 | |
| 133 DISALLOW_COPY_AND_ASSIGN(HangingHostResolverWithCancel); | |
| 134 }; | |
| 135 | |
| 136 // Tests a complete handshake and the disconnection. | |
| 137 TEST_F(SOCKSClientSocketTest, CompleteHandshake) { | |
| 138 const std::string payload_write = "random data"; | |
| 139 const std::string payload_read = "moar random data"; | |
| 140 | |
| 141 MockWrite data_writes[] = { | |
| 142 MockWrite(ASYNC, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)), | |
| 143 MockWrite(ASYNC, payload_write.data(), payload_write.size()) }; | |
| 144 MockRead data_reads[] = { | |
| 145 MockRead(ASYNC, kSOCKSOkReply, arraysize(kSOCKSOkReply)), | |
| 146 MockRead(ASYNC, payload_read.data(), payload_read.size()) }; | |
| 147 CapturingNetLog log; | |
| 148 | |
| 149 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 150 data_writes, arraysize(data_writes), | |
| 151 host_resolver_.get(), | |
| 152 "localhost", 80, | |
| 153 &log); | |
| 154 | |
| 155 // At this state the TCP connection is completed but not the SOCKS handshake. | |
| 156 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
| 157 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 158 | |
| 159 int rv = user_sock_->Connect(callback_.callback()); | |
| 160 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 161 | |
| 162 CapturingNetLog::CapturedEntryList entries; | |
| 163 log.GetEntries(&entries); | |
| 164 EXPECT_TRUE( | |
| 165 LogContainsBeginEvent(entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 166 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 167 | |
| 168 rv = callback_.WaitForResult(); | |
| 169 EXPECT_EQ(OK, rv); | |
| 170 EXPECT_TRUE(user_sock_->IsConnected()); | |
| 171 log.GetEntries(&entries); | |
| 172 EXPECT_TRUE(LogContainsEndEvent( | |
| 173 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 174 | |
| 175 scoped_refptr<IOBuffer> buffer(new IOBuffer(payload_write.size())); | |
| 176 memcpy(buffer->data(), payload_write.data(), payload_write.size()); | |
| 177 rv = user_sock_->Write( | |
| 178 buffer.get(), payload_write.size(), callback_.callback()); | |
| 179 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 180 rv = callback_.WaitForResult(); | |
| 181 EXPECT_EQ(static_cast<int>(payload_write.size()), rv); | |
| 182 | |
| 183 buffer = new IOBuffer(payload_read.size()); | |
| 184 rv = | |
| 185 user_sock_->Read(buffer.get(), payload_read.size(), callback_.callback()); | |
| 186 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 187 rv = callback_.WaitForResult(); | |
| 188 EXPECT_EQ(static_cast<int>(payload_read.size()), rv); | |
| 189 EXPECT_EQ(payload_read, std::string(buffer->data(), payload_read.size())); | |
| 190 | |
| 191 user_sock_->Disconnect(); | |
| 192 EXPECT_FALSE(tcp_sock_->IsConnected()); | |
| 193 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 194 } | |
| 195 | |
| 196 // List of responses from the socks server and the errors they should | |
| 197 // throw up are tested here. | |
| 198 TEST_F(SOCKSClientSocketTest, HandshakeFailures) { | |
| 199 const struct { | |
| 200 const char fail_reply[8]; | |
| 201 Error fail_code; | |
| 202 } tests[] = { | |
| 203 // Failure of the server response code | |
| 204 { | |
| 205 { 0x01, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }, | |
| 206 ERR_SOCKS_CONNECTION_FAILED, | |
| 207 }, | |
| 208 // Failure of the null byte | |
| 209 { | |
| 210 { 0x00, 0x5B, 0x00, 0x00, 0, 0, 0, 0 }, | |
| 211 ERR_SOCKS_CONNECTION_FAILED, | |
| 212 }, | |
| 213 }; | |
| 214 | |
| 215 //--------------------------------------- | |
| 216 | |
| 217 for (size_t i = 0; i < arraysize(tests); ++i) { | |
| 218 MockWrite data_writes[] = { | |
| 219 MockWrite(SYNCHRONOUS, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
| 220 MockRead data_reads[] = { | |
| 221 MockRead(SYNCHRONOUS, tests[i].fail_reply, | |
| 222 arraysize(tests[i].fail_reply)) }; | |
| 223 CapturingNetLog log; | |
| 224 | |
| 225 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 226 data_writes, arraysize(data_writes), | |
| 227 host_resolver_.get(), | |
| 228 "localhost", 80, | |
| 229 &log); | |
| 230 | |
| 231 int rv = user_sock_->Connect(callback_.callback()); | |
| 232 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 233 | |
| 234 CapturingNetLog::CapturedEntryList entries; | |
| 235 log.GetEntries(&entries); | |
| 236 EXPECT_TRUE(LogContainsBeginEvent( | |
| 237 entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 238 | |
| 239 rv = callback_.WaitForResult(); | |
| 240 EXPECT_EQ(tests[i].fail_code, rv); | |
| 241 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 242 EXPECT_TRUE(tcp_sock_->IsConnected()); | |
| 243 log.GetEntries(&entries); | |
| 244 EXPECT_TRUE(LogContainsEndEvent( | |
| 245 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 // Tests scenario when the server sends the handshake response in | |
| 250 // more than one packet. | |
| 251 TEST_F(SOCKSClientSocketTest, PartialServerReads) { | |
| 252 const char kSOCKSPartialReply1[] = { 0x00 }; | |
| 253 const char kSOCKSPartialReply2[] = { 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; | |
| 254 | |
| 255 MockWrite data_writes[] = { | |
| 256 MockWrite(ASYNC, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
| 257 MockRead data_reads[] = { | |
| 258 MockRead(ASYNC, kSOCKSPartialReply1, arraysize(kSOCKSPartialReply1)), | |
| 259 MockRead(ASYNC, kSOCKSPartialReply2, arraysize(kSOCKSPartialReply2)) }; | |
| 260 CapturingNetLog log; | |
| 261 | |
| 262 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 263 data_writes, arraysize(data_writes), | |
| 264 host_resolver_.get(), | |
| 265 "localhost", 80, | |
| 266 &log); | |
| 267 | |
| 268 int rv = user_sock_->Connect(callback_.callback()); | |
| 269 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 270 CapturingNetLog::CapturedEntryList entries; | |
| 271 log.GetEntries(&entries); | |
| 272 EXPECT_TRUE(LogContainsBeginEvent( | |
| 273 entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 274 | |
| 275 rv = callback_.WaitForResult(); | |
| 276 EXPECT_EQ(OK, rv); | |
| 277 EXPECT_TRUE(user_sock_->IsConnected()); | |
| 278 log.GetEntries(&entries); | |
| 279 EXPECT_TRUE(LogContainsEndEvent( | |
| 280 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 281 } | |
| 282 | |
| 283 // Tests scenario when the client sends the handshake request in | |
| 284 // more than one packet. | |
| 285 TEST_F(SOCKSClientSocketTest, PartialClientWrites) { | |
| 286 const char kSOCKSPartialRequest1[] = { 0x04, 0x01 }; | |
| 287 const char kSOCKSPartialRequest2[] = { 0x00, 0x50, 127, 0, 0, 1, 0 }; | |
| 288 | |
| 289 MockWrite data_writes[] = { | |
| 290 MockWrite(ASYNC, kSOCKSPartialRequest1, arraysize(kSOCKSPartialRequest1)), | |
| 291 // simulate some empty writes | |
| 292 MockWrite(ASYNC, 0), | |
| 293 MockWrite(ASYNC, 0), | |
| 294 MockWrite(ASYNC, kSOCKSPartialRequest2, arraysize(kSOCKSPartialRequest2)), | |
| 295 }; | |
| 296 MockRead data_reads[] = { | |
| 297 MockRead(ASYNC, kSOCKSOkReply, arraysize(kSOCKSOkReply)) }; | |
| 298 CapturingNetLog log; | |
| 299 | |
| 300 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 301 data_writes, arraysize(data_writes), | |
| 302 host_resolver_.get(), | |
| 303 "localhost", 80, | |
| 304 &log); | |
| 305 | |
| 306 int rv = user_sock_->Connect(callback_.callback()); | |
| 307 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 308 CapturingNetLog::CapturedEntryList entries; | |
| 309 log.GetEntries(&entries); | |
| 310 EXPECT_TRUE(LogContainsBeginEvent( | |
| 311 entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 312 | |
| 313 rv = callback_.WaitForResult(); | |
| 314 EXPECT_EQ(OK, rv); | |
| 315 EXPECT_TRUE(user_sock_->IsConnected()); | |
| 316 log.GetEntries(&entries); | |
| 317 EXPECT_TRUE(LogContainsEndEvent( | |
| 318 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 319 } | |
| 320 | |
| 321 // Tests the case when the server sends a smaller sized handshake data | |
| 322 // and closes the connection. | |
| 323 TEST_F(SOCKSClientSocketTest, FailedSocketRead) { | |
| 324 MockWrite data_writes[] = { | |
| 325 MockWrite(ASYNC, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) }; | |
| 326 MockRead data_reads[] = { | |
| 327 MockRead(ASYNC, kSOCKSOkReply, arraysize(kSOCKSOkReply) - 2), | |
| 328 // close connection unexpectedly | |
| 329 MockRead(SYNCHRONOUS, 0) }; | |
| 330 CapturingNetLog log; | |
| 331 | |
| 332 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 333 data_writes, arraysize(data_writes), | |
| 334 host_resolver_.get(), | |
| 335 "localhost", 80, | |
| 336 &log); | |
| 337 | |
| 338 int rv = user_sock_->Connect(callback_.callback()); | |
| 339 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 340 CapturingNetLog::CapturedEntryList entries; | |
| 341 log.GetEntries(&entries); | |
| 342 EXPECT_TRUE(LogContainsBeginEvent( | |
| 343 entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 344 | |
| 345 rv = callback_.WaitForResult(); | |
| 346 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv); | |
| 347 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 348 log.GetEntries(&entries); | |
| 349 EXPECT_TRUE(LogContainsEndEvent( | |
| 350 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 351 } | |
| 352 | |
| 353 // Tries to connect to an unknown hostname. Should fail rather than | |
| 354 // falling back to SOCKS4a. | |
| 355 TEST_F(SOCKSClientSocketTest, FailedDNS) { | |
| 356 const char hostname[] = "unresolved.ipv4.address"; | |
| 357 | |
| 358 host_resolver_->rules()->AddSimulatedFailure(hostname); | |
| 359 | |
| 360 CapturingNetLog log; | |
| 361 | |
| 362 user_sock_ = BuildMockSocket(NULL, 0, | |
| 363 NULL, 0, | |
| 364 host_resolver_.get(), | |
| 365 hostname, 80, | |
| 366 &log); | |
| 367 | |
| 368 int rv = user_sock_->Connect(callback_.callback()); | |
| 369 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 370 CapturingNetLog::CapturedEntryList entries; | |
| 371 log.GetEntries(&entries); | |
| 372 EXPECT_TRUE(LogContainsBeginEvent( | |
| 373 entries, 0, NetLog::TYPE_SOCKS_CONNECT)); | |
| 374 | |
| 375 rv = callback_.WaitForResult(); | |
| 376 EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); | |
| 377 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 378 log.GetEntries(&entries); | |
| 379 EXPECT_TRUE(LogContainsEndEvent( | |
| 380 entries, -1, NetLog::TYPE_SOCKS_CONNECT)); | |
| 381 } | |
| 382 | |
| 383 // Calls Disconnect() while a host resolve is in progress. The outstanding host | |
| 384 // resolve should be cancelled. | |
| 385 TEST_F(SOCKSClientSocketTest, DisconnectWhileHostResolveInProgress) { | |
| 386 scoped_ptr<HangingHostResolverWithCancel> hanging_resolver( | |
| 387 new HangingHostResolverWithCancel()); | |
| 388 | |
| 389 // Doesn't matter what the socket data is, we will never use it -- garbage. | |
| 390 MockWrite data_writes[] = { MockWrite(SYNCHRONOUS, "", 0) }; | |
| 391 MockRead data_reads[] = { MockRead(SYNCHRONOUS, "", 0) }; | |
| 392 | |
| 393 user_sock_ = BuildMockSocket(data_reads, arraysize(data_reads), | |
| 394 data_writes, arraysize(data_writes), | |
| 395 hanging_resolver.get(), | |
| 396 "foo", 80, | |
| 397 NULL); | |
| 398 | |
| 399 // Start connecting (will get stuck waiting for the host to resolve). | |
| 400 int rv = user_sock_->Connect(callback_.callback()); | |
| 401 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 402 | |
| 403 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 404 EXPECT_FALSE(user_sock_->IsConnectedAndIdle()); | |
| 405 | |
| 406 // The host resolver should have received the resolve request. | |
| 407 EXPECT_TRUE(hanging_resolver->HasOutstandingRequest()); | |
| 408 | |
| 409 // Disconnect the SOCKS socket -- this should cancel the outstanding resolve. | |
| 410 user_sock_->Disconnect(); | |
| 411 | |
| 412 EXPECT_FALSE(hanging_resolver->HasOutstandingRequest()); | |
| 413 | |
| 414 EXPECT_FALSE(user_sock_->IsConnected()); | |
| 415 EXPECT_FALSE(user_sock_->IsConnectedAndIdle()); | |
| 416 } | |
| 417 | |
| 418 // Tries to connect to an IPv6 IP. Should fail, as SOCKS4 does not support | |
| 419 // IPv6. | |
| 420 TEST_F(SOCKSClientSocketTest, NoIPv6) { | |
| 421 const char kHostName[] = "::1"; | |
| 422 | |
| 423 user_sock_ = BuildMockSocket(NULL, 0, | |
| 424 NULL, 0, | |
| 425 host_resolver_.get(), | |
| 426 kHostName, 80, | |
| 427 NULL); | |
| 428 | |
| 429 EXPECT_EQ(ERR_NAME_NOT_RESOLVED, | |
| 430 callback_.GetResult(user_sock_->Connect(callback_.callback()))); | |
| 431 } | |
| 432 | |
| 433 // Same as above, but with a real resolver, to protect against regressions. | |
| 434 TEST_F(SOCKSClientSocketTest, NoIPv6RealResolver) { | |
| 435 const char kHostName[] = "::1"; | |
| 436 | |
| 437 scoped_ptr<HostResolver> host_resolver( | |
| 438 HostResolver::CreateSystemResolver(HostResolver::Options(), NULL)); | |
| 439 | |
| 440 user_sock_ = BuildMockSocket(NULL, 0, | |
| 441 NULL, 0, | |
| 442 host_resolver.get(), | |
| 443 kHostName, 80, | |
| 444 NULL); | |
| 445 | |
| 446 EXPECT_EQ(ERR_NAME_NOT_RESOLVED, | |
| 447 callback_.GetResult(user_sock_->Connect(callback_.callback()))); | |
| 448 } | |
| 449 | |
| 450 } // namespace net | |
| OLD | NEW |