diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-01 02:12:45 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-01 02:12:45 +0000 |
commit | 8a91a0a06c35b1487949c4037954e08172827f90 (patch) | |
tree | 6583e95c3a82749a73a0dbac57f354839ab4446d /jingle | |
parent | 29c6bb325b65ce953ed11cc344455e67be697b84 (diff) | |
download | chromium_src-8a91a0a06c35b1487949c4037954e08172827f90.zip chromium_src-8a91a0a06c35b1487949c4037954e08172827f90.tar.gz chromium_src-8a91a0a06c35b1487949c4037954e08172827f90.tar.bz2 |
Added FakeSSLClentSocket for connecting to Google Talk on the SSL port
FakeSSLClientSocket is a wrapper around a ClientSocket that simulates
an SSL handshake, which is needed to connect to Google Talk servers
on the SSL port. This was adapted from the libjingle socket
implementation.
BUG=45612
TEST=new unit tests
Review URL: http://codereview.chromium.org/3231006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58110 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'jingle')
-rw-r--r-- | jingle/jingle.gyp | 3 | ||||
-rw-r--r-- | jingle/notifier/base/fake_ssl_client_socket.cc | 327 | ||||
-rw-r--r-- | jingle/notifier/base/fake_ssl_client_socket.h | 124 | ||||
-rw-r--r-- | jingle/notifier/base/fake_ssl_client_socket_unittest.cc | 339 |
4 files changed, 793 insertions, 0 deletions
diff --git a/jingle/jingle.gyp b/jingle/jingle.gyp index eafe96b..0b093fa 100644 --- a/jingle/jingle.gyp +++ b/jingle/jingle.gyp @@ -20,6 +20,8 @@ 'notifier/base/signal_thread_task.h', 'notifier/base/ssl_adapter.h', 'notifier/base/ssl_adapter.cc', + 'notifier/base/fake_ssl_client_socket.cc', + 'notifier/base/fake_ssl_client_socket.h', 'notifier/base/static_assert.h', 'notifier/base/task_pump.cc', 'notifier/base/task_pump.h', @@ -96,6 +98,7 @@ # TODO(akalin): Write our own test suite and runner. '../base/test/run_all_unittests.cc', 'notifier/base/chrome_async_socket_unittest.cc', + 'notifier/base/fake_ssl_client_socket_unittest.cc', 'notifier/listener/talk_mediator_unittest.cc', 'notifier/listener/send_update_task_unittest.cc', 'notifier/listener/subscribe_task_unittest.cc', diff --git a/jingle/notifier/base/fake_ssl_client_socket.cc b/jingle/notifier/base/fake_ssl_client_socket.cc new file mode 100644 index 0000000..a89a594 --- /dev/null +++ b/jingle/notifier/base/fake_ssl_client_socket.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "jingle/notifier/base/fake_ssl_client_socket.h" + +#include <cstdlib> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" + +namespace notifier { + +namespace { + +// The constants below were taken from libjingle's socketadapters.cc. +// Basically, we do a "fake" SSL handshake to fool proxies into +// thinking this is a real SSL connection. + +// This is a SSL v2 CLIENT_HELLO message. +// TODO(juberti): Should this have a session id? The response doesn't have a +// certificate, so the hello should have a session id. +static const uint8 kSslClientHello[] = { + 0x80, 0x46, // msg len + 0x01, // CLIENT_HELLO + 0x03, 0x01, // SSL 3.1 + 0x00, 0x2d, // ciphersuite len + 0x00, 0x00, // session id len + 0x00, 0x10, // challenge len + 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0, // ciphersuites + 0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, // + 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, // + 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, // + 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, // + 0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, // challenge + 0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea // +}; + +// This is a TLSv1 SERVER_HELLO message. +static const uint8 kSslServerHello[] = { + 0x16, // handshake message + 0x03, 0x01, // SSL 3.1 + 0x00, 0x4a, // message len + 0x02, // SERVER_HELLO + 0x00, 0x00, 0x46, // handshake len + 0x03, 0x01, // SSL 3.1 + 0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0, // server random + 0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, // + 0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1, // + 0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f, // + 0x20, // session id len + 0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f, // session id + 0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b, // + 0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38, // + 0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c, // + 0x00, 0x04, // RSA/RC4-128/MD5 + 0x00 // null compression +}; + +net::DrainableIOBuffer* NewDrainableIOBufferWithSize(int size) { + return new net::DrainableIOBuffer(new net::IOBuffer(size), size); +} + +} // namespace + +base::StringPiece FakeSSLClientSocket::GetSslClientHello() { + return base::StringPiece(reinterpret_cast<const char*>(kSslClientHello), + arraysize(kSslClientHello)); +} + +base::StringPiece FakeSSLClientSocket::GetSslServerHello() { + return base::StringPiece(reinterpret_cast<const char*>(kSslServerHello), + arraysize(kSslServerHello)); +} + +FakeSSLClientSocket::FakeSSLClientSocket( + net::ClientSocket* transport_socket) + : connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), + &FakeSSLClientSocket::OnConnectDone), + send_client_hello_callback_( + ALLOW_THIS_IN_INITIALIZER_LIST(this), + &FakeSSLClientSocket::OnSendClientHelloDone), + verify_server_hello_callback_( + ALLOW_THIS_IN_INITIALIZER_LIST(this), + &FakeSSLClientSocket::OnVerifyServerHelloDone), + transport_socket_(transport_socket), + next_handshake_state_(STATE_NONE), + handshake_completed_(false), + user_connect_callback_(NULL), + write_buf_(NewDrainableIOBufferWithSize(arraysize(kSslClientHello))), + read_buf_(NewDrainableIOBufferWithSize(arraysize(kSslServerHello))) { + CHECK(transport_socket_.get()); + std::memcpy(write_buf_->data(), kSslClientHello, arraysize(kSslClientHello)); +} + +FakeSSLClientSocket::~FakeSSLClientSocket() {} + +int FakeSSLClientSocket::Read(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback) { + DCHECK_EQ(next_handshake_state_, STATE_NONE); + DCHECK(handshake_completed_); + return transport_socket_->Read(buf, buf_len, callback); +} + +int FakeSSLClientSocket::Write(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback) { + DCHECK_EQ(next_handshake_state_, STATE_NONE); + DCHECK(handshake_completed_); + return transport_socket_->Write(buf, buf_len, callback); +} + +bool FakeSSLClientSocket::SetReceiveBufferSize(int32 size) { + return transport_socket_->SetReceiveBufferSize(size); +} + +bool FakeSSLClientSocket::SetSendBufferSize(int32 size) { + return transport_socket_->SetSendBufferSize(size); +} + +int FakeSSLClientSocket::Connect(net::CompletionCallback* callback) { + // We don't support synchronous operation, even if + // |transport_socket_| does. + DCHECK(callback); + DCHECK_EQ(next_handshake_state_, STATE_NONE); + DCHECK(!handshake_completed_); + DCHECK(!user_connect_callback_); + DCHECK_EQ(write_buf_->BytesConsumed(), 0); + DCHECK_EQ(read_buf_->BytesConsumed(), 0); + + next_handshake_state_ = STATE_CONNECT; + int status = DoHandshakeLoop(); + if (status == net::ERR_IO_PENDING) { + user_connect_callback_ = callback; + } + return status; +} + +int FakeSSLClientSocket::DoHandshakeLoop() { + DCHECK_NE(next_handshake_state_, STATE_NONE); + int status = net::OK; + do { + HandshakeState state = next_handshake_state_; + next_handshake_state_ = STATE_NONE; + switch (state) { + case STATE_CONNECT: + status = DoConnect(); + break; + case STATE_SEND_CLIENT_HELLO: + status = DoSendClientHello(); + break; + case STATE_VERIFY_SERVER_HELLO: + status = DoVerifyServerHello(); + break; + default: + status = net::ERR_UNEXPECTED; + LOG(DFATAL) << "unexpected state: " << state; + break; + } + } while ((status != net::ERR_IO_PENDING) && + (next_handshake_state_ != STATE_NONE)); + return status; +} + +void FakeSSLClientSocket::RunUserConnectCallback(int status) { + DCHECK_LE(status, net::OK); + next_handshake_state_ = STATE_NONE; + net::CompletionCallback* user_connect_callback = user_connect_callback_; + user_connect_callback_ = NULL; + user_connect_callback->Run(status); +} + +void FakeSSLClientSocket::DoHandshakeLoopWithUserConnectCallback() { + int status = DoHandshakeLoop(); + if (status != net::ERR_IO_PENDING) { + RunUserConnectCallback(status); + } +} + +int FakeSSLClientSocket::DoConnect() { + int status = transport_socket_->Connect(&connect_callback_); + if (status != net::OK) { + return status; + } + ProcessConnectDone(); + return net::OK; +} + +void FakeSSLClientSocket::OnConnectDone(int status) { + DCHECK_NE(status, net::ERR_IO_PENDING); + DCHECK_LE(status, net::OK); + DCHECK(user_connect_callback_); + if (status != net::OK) { + RunUserConnectCallback(status); + return; + } + ProcessConnectDone(); + DoHandshakeLoopWithUserConnectCallback(); +} + +void FakeSSLClientSocket::ProcessConnectDone() { + DCHECK_EQ(write_buf_->BytesConsumed(), 0); + DCHECK_EQ(read_buf_->BytesConsumed(), 0); + next_handshake_state_ = STATE_SEND_CLIENT_HELLO; +} + +int FakeSSLClientSocket::DoSendClientHello() { + int status = transport_socket_->Write( + write_buf_, write_buf_->BytesRemaining(), + &send_client_hello_callback_); + if (status < net::OK) { + return status; + } + ProcessSendClientHelloDone(static_cast<size_t>(status)); + return net::OK; +} + +void FakeSSLClientSocket::OnSendClientHelloDone(int status) { + DCHECK_NE(status, net::ERR_IO_PENDING); + DCHECK(user_connect_callback_); + if (status < net::OK) { + RunUserConnectCallback(status); + return; + } + ProcessSendClientHelloDone(static_cast<size_t>(status)); + DoHandshakeLoopWithUserConnectCallback(); +} + +void FakeSSLClientSocket::ProcessSendClientHelloDone(size_t written) { + DCHECK_LE(written, static_cast<size_t>(write_buf_->BytesRemaining())); + DCHECK_EQ(read_buf_->BytesConsumed(), 0); + if (written < static_cast<size_t>(write_buf_->BytesRemaining())) { + next_handshake_state_ = STATE_SEND_CLIENT_HELLO; + write_buf_->DidConsume(written); + } else { + next_handshake_state_ = STATE_VERIFY_SERVER_HELLO; + } +} + +int FakeSSLClientSocket::DoVerifyServerHello() { + int status = transport_socket_->Read( + read_buf_, read_buf_->BytesRemaining(), + &verify_server_hello_callback_); + if (status < net::OK) { + return status; + } + size_t read = static_cast<size_t>(status); + return ProcessVerifyServerHelloDone(read); +} + +void FakeSSLClientSocket::OnVerifyServerHelloDone(int status) { + DCHECK_NE(status, net::ERR_IO_PENDING); + DCHECK(user_connect_callback_); + if (status < net::OK) { + RunUserConnectCallback(status); + return; + } + size_t read = static_cast<size_t>(status); + status = ProcessVerifyServerHelloDone(read); + if (status < net::OK) { + RunUserConnectCallback(status); + return; + } + if (handshake_completed_) { + RunUserConnectCallback(net::OK); + } else { + DoHandshakeLoopWithUserConnectCallback(); + } +} + +net::Error FakeSSLClientSocket::ProcessVerifyServerHelloDone(size_t read) { + DCHECK_LE(read, static_cast<size_t>(read_buf_->BytesRemaining())); + if (read == 0U) { + return net::ERR_UNEXPECTED; + } + const uint8* expected_data_start = + &kSslServerHello[arraysize(kSslServerHello) - + read_buf_->BytesRemaining()]; + if (std::memcmp(expected_data_start, read_buf_->data(), read) != 0) { + return net::ERR_UNEXPECTED; + } + if (read < static_cast<size_t>(read_buf_->BytesRemaining())) { + next_handshake_state_ = STATE_VERIFY_SERVER_HELLO; + read_buf_->DidConsume(read); + } else { + next_handshake_state_ = STATE_NONE; + handshake_completed_ = true; + } + return net::OK; +} + +void FakeSSLClientSocket::Disconnect() { + transport_socket_->Disconnect(); + next_handshake_state_ = STATE_NONE; + handshake_completed_ = false; + user_connect_callback_ = NULL; + write_buf_->SetOffset(0); + read_buf_->SetOffset(0); +} + +bool FakeSSLClientSocket::IsConnected() const { + return handshake_completed_ && transport_socket_->IsConnected(); +} + +bool FakeSSLClientSocket::IsConnectedAndIdle() const { + return handshake_completed_ && transport_socket_->IsConnectedAndIdle(); +} + +int FakeSSLClientSocket::GetPeerAddress(net::AddressList* address) const { + return transport_socket_->GetPeerAddress(address); +} + +const net::BoundNetLog& FakeSSLClientSocket::NetLog() const { + return transport_socket_->NetLog(); +} + +void FakeSSLClientSocket::SetSubresourceSpeculation() { + transport_socket_->SetSubresourceSpeculation(); +} + +void FakeSSLClientSocket::SetOmniboxSpeculation() { + transport_socket_->SetOmniboxSpeculation(); +} + +} // namespace notifier diff --git a/jingle/notifier/base/fake_ssl_client_socket.h b/jingle/notifier/base/fake_ssl_client_socket.h new file mode 100644 index 0000000..e4765c5 --- /dev/null +++ b/jingle/notifier/base/fake_ssl_client_socket.h @@ -0,0 +1,124 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This ClientSocket implementation is to be used with servers that +// accept connections on port 443 but don't really use SSL. For +// example, the Google Talk servers do this to bypass proxies. (The +// connection is upgraded to TLS as part of the XMPP negotiation, so +// security is preserved.) A "fake" SSL handshake is done immediately +// after connection to fool proxies into thinking that this is a real +// SSL connection. +// +// NOTE: This ClientSocket implementation does *not* do a real SSL +// handshake nor does it do any encryption! + +#ifndef JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_ +#define JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_ +#pragma once + +#include <cstddef> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/ref_counted.h" +#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" +#include "net/socket/client_socket.h" + +namespace net { +class DrainableIOBuffer; +} // namespace net + +namespace notifier { + +class FakeSSLClientSocket : public net::ClientSocket { + public: + // Takes ownership of |transport_socket|. + explicit FakeSSLClientSocket(net::ClientSocket* transport_socket); + + virtual ~FakeSSLClientSocket(); + + // Exposed for testing. + static base::StringPiece GetSslClientHello(); + static base::StringPiece GetSslServerHello(); + + // net::ClientSocket implementation. + + virtual int Read(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback); + + virtual int Write(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback); + + virtual bool SetReceiveBufferSize(int32 size); + + virtual bool SetSendBufferSize(int32 size); + + virtual int Connect(net::CompletionCallback* callback); + + virtual void Disconnect(); + + virtual bool IsConnected() const; + + virtual bool IsConnectedAndIdle() const; + + virtual int GetPeerAddress(net::AddressList* address) const; + + virtual const net::BoundNetLog& NetLog() const; + + virtual void SetSubresourceSpeculation(); + + virtual void SetOmniboxSpeculation(); + + private: + enum HandshakeState { + STATE_NONE, + STATE_CONNECT, + STATE_SEND_CLIENT_HELLO, + STATE_VERIFY_SERVER_HELLO, + }; + + int DoHandshakeLoop(); + void RunUserConnectCallback(int status); + void DoHandshakeLoopWithUserConnectCallback(); + + int DoConnect(); + void OnConnectDone(int status); + void ProcessConnectDone(); + + int DoSendClientHello(); + void OnSendClientHelloDone(int status); + void ProcessSendClientHelloDone(size_t written); + + int DoVerifyServerHello(); + void OnVerifyServerHelloDone(int status); + net::Error ProcessVerifyServerHelloDone(size_t read); + + // Callbacks passed to |transport_socket_|. + net::CompletionCallbackImpl<FakeSSLClientSocket> connect_callback_; + net::CompletionCallbackImpl<FakeSSLClientSocket> + send_client_hello_callback_; + net::CompletionCallbackImpl<FakeSSLClientSocket> + verify_server_hello_callback_; + + scoped_ptr<net::ClientSocket> transport_socket_; + + // During the handshake process, holds a value from HandshakeState. + // STATE_NONE otherwise. + HandshakeState next_handshake_state_; + + // True iff we're connected and we've finished the handshake. + bool handshake_completed_; + + // The callback passed to Connect(). + net::CompletionCallback* user_connect_callback_; + + scoped_refptr<net::DrainableIOBuffer> write_buf_; + scoped_refptr<net::DrainableIOBuffer> read_buf_; +}; + +} // namespace notifier + +#endif // JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_ diff --git a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc new file mode 100644 index 0000000..2ca3adf --- /dev/null +++ b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc @@ -0,0 +1,339 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "jingle/notifier/base/fake_ssl_client_socket.h" + +#include <algorithm> +#include <vector> + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "net/base/capturing_net_log.h" +#include "net/base/io_buffer.h" +#include "net/base/net_log.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/client_socket.h" +#include "net/socket/socket_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace notifier { + +namespace { + +using ::testing::Return; +using ::testing::ReturnRef; + +// Used by RunUnsuccessfulHandshakeTestHelper. Represents where in +// the handshake step an error should be inserted. +enum HandshakeErrorLocation { + CONNECT_ERROR, + SEND_CLIENT_HELLO_ERROR, + VERIFY_SERVER_HELLO_ERROR, +}; + +// Private error codes appended to the net::Error set. +enum { + // An error representing a server hello that has been corrupted in + // transit. + ERR_MALFORMED_SERVER_HELLO = -15000, +}; + +// Used by PassThroughMethods test. +class MockClientSocket : public net::ClientSocket { + public: + virtual ~MockClientSocket() {} + + MOCK_METHOD3(Read, int(net::IOBuffer*, int, net::CompletionCallback*)); + MOCK_METHOD3(Write, int(net::IOBuffer*, int, net::CompletionCallback*)); + MOCK_METHOD1(SetReceiveBufferSize, bool(int32)); + MOCK_METHOD1(SetSendBufferSize, bool(int32)); + MOCK_METHOD1(Connect, int(net::CompletionCallback*)); + MOCK_METHOD0(Disconnect, void()); + MOCK_CONST_METHOD0(IsConnected, bool()); + MOCK_CONST_METHOD0(IsConnectedAndIdle, bool()); + MOCK_CONST_METHOD1(GetPeerAddress, int(net::AddressList*)); + MOCK_CONST_METHOD0(NetLog, const net::BoundNetLog&()); + MOCK_METHOD0(SetSubresourceSpeculation, void()); + MOCK_METHOD0(SetOmniboxSpeculation, void()); +}; + +// Break up |data| into a bunch of chunked MockReads/Writes and push +// them onto |ops|. +void AddChunkedOps(base::StringPiece data, size_t chunk_size, bool async, + std::vector<net::MockRead>* ops) { + DCHECK_GT(chunk_size, 0U); + size_t offset = 0; + while (offset < data.size()) { + size_t bounded_chunk_size = std::min(data.size() - offset, chunk_size); + // We take advantage of the fact that MockWrite is typedefed to + // MockRead. + ops->push_back(net::MockRead(async, data.data() + offset, + bounded_chunk_size)); + offset += bounded_chunk_size; + } +} + +class FakeSSLClientSocketTest : public testing::Test { + protected: + FakeSSLClientSocketTest() + : capturing_net_log_(net::CapturingNetLog::kUnbounded) {} + + virtual ~FakeSSLClientSocketTest() {} + + net::ClientSocket* MakeClientSocket() { + return mock_client_socket_factory_.CreateTCPClientSocket( + net::AddressList(), &capturing_net_log_, net::NetLog::Source()); + } + + void SetData(const net::MockConnect& mock_connect, + std::vector<net::MockRead>* reads, + std::vector<net::MockWrite>* writes) { + static_socket_data_provider_.reset( + new net::StaticSocketDataProvider( + &*reads->begin(), reads->size(), + &*writes->begin(), writes->size())); + static_socket_data_provider_->set_connect_data(mock_connect); + mock_client_socket_factory_.AddSocketDataProvider( + static_socket_data_provider_.get()); + } + + void ExpectStatus( + bool async, int expected_status, int immediate_status, + TestCompletionCallback* test_completion_callback) { + if (async) { + EXPECT_EQ(net::ERR_IO_PENDING, immediate_status); + int status = test_completion_callback->WaitForResult(); + EXPECT_EQ(expected_status, status); + } else { + EXPECT_EQ(expected_status, immediate_status); + } + } + + // Sets up the mock socket to generate a successful handshake + // (sliced up according to the parameters) and makes sure the + // FakeSSLClientSocket behaves as expected. + void RunSuccessfulHandshakeTest( + bool async, size_t read_chunk_size, size_t write_chunk_size, + int num_resets) { + base::StringPiece ssl_client_hello = + FakeSSLClientSocket::GetSslClientHello(); + base::StringPiece ssl_server_hello = + FakeSSLClientSocket::GetSslServerHello(); + + net::MockConnect mock_connect(async, net::OK); + std::vector<net::MockRead> reads; + std::vector<net::MockWrite> writes; + static const char kReadTestData[] = "read test data"; + static const char kWriteTestData[] = "write test data"; + for (int i = 0; i < num_resets + 1; ++i) { + SCOPED_TRACE(i); + AddChunkedOps(ssl_server_hello, read_chunk_size, async, &reads); + AddChunkedOps(ssl_client_hello, write_chunk_size, async, &writes); + reads.push_back( + net::MockRead(async, kReadTestData, arraysize(kReadTestData))); + writes.push_back( + net::MockWrite(async, kWriteTestData, arraysize(kWriteTestData))); + } + SetData(mock_connect, &reads, &writes); + + FakeSSLClientSocket fake_ssl_client_socket(MakeClientSocket()); + + for (int i = 0; i < num_resets + 1; ++i) { + SCOPED_TRACE(i); + TestCompletionCallback test_completion_callback; + int status = fake_ssl_client_socket.Connect(&test_completion_callback); + if (async) { + EXPECT_FALSE(fake_ssl_client_socket.IsConnected()); + } + ExpectStatus(async, net::OK, status, &test_completion_callback); + if (fake_ssl_client_socket.IsConnected()) { + int read_len = arraysize(kReadTestData); + int read_buf_len = 2 * read_len; + scoped_refptr<net::IOBuffer> read_buf( + new net::IOBuffer(read_buf_len)); + int read_status = fake_ssl_client_socket.Read( + read_buf, read_buf_len, &test_completion_callback); + ExpectStatus(async, read_len, read_status, &test_completion_callback); + + scoped_refptr<net::IOBuffer> write_buf( + new net::StringIOBuffer(kWriteTestData)); + int write_status = fake_ssl_client_socket.Write( + write_buf, arraysize(kWriteTestData), &test_completion_callback); + ExpectStatus(async, arraysize(kWriteTestData), write_status, + &test_completion_callback); + } else { + ADD_FAILURE(); + } + fake_ssl_client_socket.Disconnect(); + EXPECT_FALSE(fake_ssl_client_socket.IsConnected()); + } + } + + // Sets up the mock socket to generate an unsuccessful handshake + // FakeSSLClientSocket fails as expected. + void RunUnsuccessfulHandshakeTestHelper( + bool async, int error, HandshakeErrorLocation location) { + DCHECK_NE(error, net::OK); + base::StringPiece ssl_client_hello = + FakeSSLClientSocket::GetSslClientHello(); + base::StringPiece ssl_server_hello = + FakeSSLClientSocket::GetSslServerHello(); + + net::MockConnect mock_connect(async, net::OK); + std::vector<net::MockRead> reads; + std::vector<net::MockWrite> writes; + const size_t kChunkSize = 1; + AddChunkedOps(ssl_server_hello, kChunkSize, async, &reads); + AddChunkedOps(ssl_client_hello, kChunkSize, async, &writes); + switch (location) { + case CONNECT_ERROR: + mock_connect.result = error; + writes.clear(); + reads.clear(); + break; + case SEND_CLIENT_HELLO_ERROR: { + // Use a fixed index for repeatability. + size_t index = 100 % writes.size(); + writes[index].result = error; + writes[index].data = NULL; + writes[index].data_len = 0; + writes.resize(index + 1); + reads.clear(); + break; + } + case VERIFY_SERVER_HELLO_ERROR: { + // Use a fixed index for repeatability. + size_t index = 50 % reads.size(); + if (error == ERR_MALFORMED_SERVER_HELLO) { + static const char kBadData[] = "BAD_DATA"; + reads[index].data = kBadData; + reads[index].data_len = arraysize(kBadData); + } else { + reads[index].result = error; + reads[index].data = NULL; + reads[index].data_len = 0; + } + reads.resize(index + 1); + if (error == + net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ) { + static const char kDummyData[] = "DUMMY"; + reads.push_back(net::MockRead(async, kDummyData)); + } + break; + } + } + SetData(mock_connect, &reads, &writes); + + FakeSSLClientSocket fake_ssl_client_socket(MakeClientSocket()); + + // The two errors below are interpreted by FakeSSLClientSocket as + // an unexpected event. + int expected_status = + ((error == net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ) || + (error == ERR_MALFORMED_SERVER_HELLO)) ? + net::ERR_UNEXPECTED : error; + + TestCompletionCallback test_completion_callback; + int status = fake_ssl_client_socket.Connect(&test_completion_callback); + EXPECT_FALSE(fake_ssl_client_socket.IsConnected()); + ExpectStatus(async, expected_status, status, &test_completion_callback); + EXPECT_FALSE(fake_ssl_client_socket.IsConnected()); + } + + void RunUnsuccessfulHandshakeTest( + int error, HandshakeErrorLocation location) { + RunUnsuccessfulHandshakeTestHelper(false, error, location); + RunUnsuccessfulHandshakeTestHelper(true, error, location); + } + + // MockTCPClientSocket needs a message loop. + MessageLoop message_loop_; + + net::CapturingNetLog capturing_net_log_; + net::MockClientSocketFactory mock_client_socket_factory_; + scoped_ptr<net::StaticSocketDataProvider> static_socket_data_provider_; +}; + +TEST_F(FakeSSLClientSocketTest, PassThroughMethods) { + MockClientSocket* mock_client_socket = new MockClientSocket(); + const int kReceiveBufferSize = 10; + const int kSendBufferSize = 20; + net::AddressList address_list; + const int kPeerAddress = 30; + net::BoundNetLog net_log; + EXPECT_CALL(*mock_client_socket, SetReceiveBufferSize(kReceiveBufferSize)); + EXPECT_CALL(*mock_client_socket, SetSendBufferSize(kSendBufferSize)); + EXPECT_CALL(*mock_client_socket, GetPeerAddress(&address_list)). + WillOnce(Return(kPeerAddress)); + EXPECT_CALL(*mock_client_socket, NetLog()).WillOnce(ReturnRef(net_log)); + EXPECT_CALL(*mock_client_socket, SetSubresourceSpeculation()); + EXPECT_CALL(*mock_client_socket, SetOmniboxSpeculation()); + + // Takes ownership of |mock_client_socket|. + FakeSSLClientSocket fake_ssl_client_socket(mock_client_socket); + fake_ssl_client_socket.SetReceiveBufferSize(kReceiveBufferSize); + fake_ssl_client_socket.SetSendBufferSize(kSendBufferSize); + EXPECT_EQ(kPeerAddress, + fake_ssl_client_socket.GetPeerAddress(&address_list)); + EXPECT_EQ(&net_log, &fake_ssl_client_socket.NetLog()); + fake_ssl_client_socket.SetSubresourceSpeculation(); + fake_ssl_client_socket.SetOmniboxSpeculation(); +} + +TEST_F(FakeSSLClientSocketTest, SuccessfulHandshakeSync) { + for (size_t i = 1; i < 100; i += 3) { + SCOPED_TRACE(i); + for (size_t j = 1; j < 100; j += 5) { + SCOPED_TRACE(j); + RunSuccessfulHandshakeTest(false, i, j, 0); + } + } +} + +TEST_F(FakeSSLClientSocketTest, SuccessfulHandshakeAsync) { + for (size_t i = 1; i < 100; i += 7) { + SCOPED_TRACE(i); + for (size_t j = 1; j < 100; j += 9) { + SCOPED_TRACE(j); + RunSuccessfulHandshakeTest(true, i, j, 0); + } + } +} + +TEST_F(FakeSSLClientSocketTest, ResetSocket) { + RunSuccessfulHandshakeTest(true, 1, 2, 3); +} + +TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeConnectError) { + RunUnsuccessfulHandshakeTest(net::ERR_ACCESS_DENIED, CONNECT_ERROR); +} + +TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeWriteError) { + RunUnsuccessfulHandshakeTest(net::ERR_OUT_OF_MEMORY, + SEND_CLIENT_HELLO_ERROR); +} + +TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeReadError) { + RunUnsuccessfulHandshakeTest(net::ERR_CONNECTION_CLOSED, + VERIFY_SERVER_HELLO_ERROR); +} + +TEST_F(FakeSSLClientSocketTest, PeerClosedDuringHandshake) { + RunUnsuccessfulHandshakeTest( + net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ, + VERIFY_SERVER_HELLO_ERROR); +} + +TEST_F(FakeSSLClientSocketTest, MalformedServerHello) { + RunUnsuccessfulHandshakeTest(ERR_MALFORMED_SERVER_HELLO, + VERIFY_SERVER_HELLO_ERROR); +} + +} // namespace + +} // namespace notifier + |