| Index: chrome/browser/extensions/api/socket/ssl_socket.cc
|
| diff --git a/chrome/browser/extensions/api/socket/ssl_socket.cc b/chrome/browser/extensions/api/socket/ssl_socket.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..511e32c945d203f435c212e2c4d983c9ec58c272
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/api/socket/ssl_socket.cc
|
| @@ -0,0 +1,325 @@
|
| +// Copyright (c) 2012 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 "chrome/browser/extensions/api/socket/ssl_socket.h"
|
| +
|
| +#include "chrome/browser/extensions/api/api_resource.h"
|
| +#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
|
| +#include "net/base/address_list.h"
|
| +#include "net/base/cert_verifier.h"
|
| +#include "net/base/host_port_pair.h"
|
| +#include "net/base/ip_endpoint.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/rand_callback.h"
|
| +#include "net/base/ssl_config_service.h"
|
| +#include "net/socket/client_socket_factory.h"
|
| +#include "net/socket/client_socket_handle.h"
|
| +#include "net/socket/tcp_client_socket.h"
|
| +
|
| +// SSLSocket starts out as a TCP socket in SSLSTATE_NONE. When a handshake
|
| +// request is made, it transitions to SSLSTATE_WAIT. If there are no pending
|
| +// read or write requests on the TCP socket, an SSL client socket is then
|
| +// created and asked to perform the handshake. Otherwise, SSLSocket
|
| +// continues to attempt to start the handshake after every pending read
|
| +// and write request completes. While in SSLSTATE_WAIT, no new read or
|
| +// write requests may be issued (and are replied to with an immediate
|
| +// net::ERR_IO_PENDING).
|
| +//
|
| +// If the SSL handshake succeeds, SSLSocket enters SSLSTATE_CONNECTED
|
| +// and all read and write requests go over the secure channel. If
|
| +// the SSL handshake fails, SSLSocket enters SSLSTATE_ERROR and no
|
| +// further read or write requests may be made until the socket is
|
| +// closed.
|
| +
|
| +namespace extensions {
|
| +
|
| +SSLSocket::SSLSocket(extensions::APIResourceEventNotifier* event_notifier)
|
| + : Socket(event_notifier),
|
| + ssl_state_(SSLSTATE_NONE) {
|
| +}
|
| +
|
| +// For testing.
|
| +SSLSocket::SSLSocket(net::TCPClientSocket* tcp_client_socket,
|
| + APIResourceEventNotifier* event_notifier)
|
| + : Socket(event_notifier),
|
| + socket_(tcp_client_socket),
|
| + ssl_state_(SSLSTATE_NONE) {
|
| +}
|
| +
|
| +// static
|
| +SSLSocket* SSLSocket::CreateSocketForTesting(
|
| + net::TCPClientSocket* tcp_client_socket,
|
| + APIResourceEventNotifier* event_notifier) {
|
| + return new SSLSocket(tcp_client_socket, event_notifier);
|
| +}
|
| +
|
| +SSLSocket::~SSLSocket() {
|
| + if (is_connected_) {
|
| + Disconnect();
|
| + }
|
| +}
|
| +
|
| +void SSLSocket::Connect(const std::string& address,
|
| + int port,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(!callback.is_null());
|
| +
|
| + if (!connect_callback_.is_null()) {
|
| + callback.Run(net::ERR_CONNECTION_FAILED);
|
| + return;
|
| + }
|
| + connect_callback_ = callback;
|
| +
|
| + int result = net::ERR_CONNECTION_FAILED;
|
| + do {
|
| + if (is_connected_)
|
| + break;
|
| +
|
| + net::AddressList address_list;
|
| + if (!StringAndPortToAddressList(address, port, &address_list)) {
|
| + result = net::ERR_ADDRESS_INVALID;
|
| + break;
|
| + }
|
| +
|
| + socket_.reset(new net::TCPClientSocket(address_list, NULL,
|
| + net::NetLog::Source()));
|
| + connect_callback_ = callback;
|
| + result = socket_->Connect(base::Bind(
|
| + &SSLSocket::OnConnectComplete, base::Unretained(this)));
|
| + } while (false);
|
| +
|
| + if (result != net::ERR_IO_PENDING)
|
| + OnConnectComplete(result);
|
| +}
|
| +
|
| +void SSLSocket::PerformSSLHandshake(const std::string& ssl_hostname,
|
| + int ssl_port,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(!callback.is_null());
|
| +
|
| + if (ssl_state_ == SSLSTATE_WAIT) {
|
| + callback.Run(net::ERR_IO_PENDING);
|
| + return;
|
| + } else if (ssl_state_ == SSLSTATE_ERROR) {
|
| + callback.Run(net::ERR_FAILED);
|
| + return;
|
| + } else if (ssl_state_ == SSLSTATE_CONNECTED) {
|
| + callback.Run(net::ERR_FAILED);
|
| + return;
|
| + }
|
| + DCHECK(ssl_state_ == SSLSTATE_NONE);
|
| +
|
| + handshake_callback_ = callback;
|
| + ssl_state_ = SSLSTATE_WAIT;
|
| + ssl_port_ = ssl_port;
|
| + ssl_hostname_ = ssl_hostname;
|
| +
|
| + AttemptSSLHandshake();
|
| +}
|
| +
|
| +void SSLSocket::AttemptSSLHandshake() {
|
| + DCHECK(ssl_state_ == SSLSTATE_WAIT);
|
| +
|
| + // We have to wait until outstanding I/O requests are
|
| + // complete before we perform the SSL handshake.
|
| + if (!read_callback_.is_null()) {
|
| + VLOG(1) << "AttemptSSLHandshake aborting (pending callback).";
|
| + return;
|
| + }
|
| +
|
| + if (!write_queue_empty()) {
|
| + VLOG(1) << "AttemptSSLHandshake aborting (pending writes).";
|
| + return;
|
| + }
|
| +
|
| + if (!socket_.get() || !socket_->IsConnected()) {
|
| + OnHandshakeComplete(net::ERR_SOCKET_NOT_CONNECTED);
|
| + return;
|
| + }
|
| +
|
| + VLOG(2) << "AttemptSSLHandshake running handshake.";
|
| +
|
| + // see remoting/jingle_glue/ssl_socket_adapter.cc
|
| + ssl_socket_.reset();
|
| + cert_verifier_.reset(net::CertVerifier::CreateDefault());
|
| +
|
| + // We'll use the default configuration to avoid issues wrt
|
| + // thread-safety and SSLConfigService.
|
| + net::SSLConfig ssl_config;
|
| + net::SSLClientSocketContext context;
|
| + context.cert_verifier = cert_verifier_.get();
|
| +
|
| + // ClientSocketHandle::set_socket takes ownership of the socket...
|
| + net::ClientSocketHandle* socket_handle = new net::ClientSocketHandle();
|
| + socket_handle->set_socket(socket_.release());
|
| +
|
| + // ... and the SSL socket takes ownership of the socket handle.
|
| + ssl_socket_.reset(
|
| + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
|
| + socket_handle,
|
| + net::HostPortPair(ssl_hostname_, ssl_port_),
|
| + ssl_config,
|
| + context));
|
| +
|
| + int result = ssl_socket_->Connect(base::Bind(
|
| + &SSLSocket::OnHandshakeComplete, base::Unretained(this)));
|
| +
|
| + if (result != net::ERR_IO_PENDING) {
|
| + OnHandshakeComplete(result);
|
| + }
|
| +}
|
| +
|
| +void SSLSocket::Disconnect() {
|
| + if (ssl_socket_.get()) {
|
| + ssl_socket_->Disconnect();
|
| + } else if (socket_.get()) {
|
| + socket_->Disconnect();
|
| + }
|
| +
|
| + ssl_state_ = SSLSTATE_NONE;
|
| + is_connected_ = false;
|
| +}
|
| +
|
| +int SSLSocket::Bind(const std::string& address, int port) {
|
| + // TODO(penghuang): Supports bind for tcp?
|
| + return net::ERR_FAILED;
|
| +}
|
| +
|
| +void SSLSocket::Read(int count,
|
| + const ReadCompletionCallback& callback) {
|
| + DCHECK(!callback.is_null());
|
| +
|
| + if (!read_callback_.is_null() || ssl_state_ == SSLSTATE_WAIT) {
|
| + callback.Run(net::ERR_IO_PENDING, NULL);
|
| + return;
|
| + } else if (ssl_state_ == SSLSTATE_ERROR) {
|
| + callback.Run(net::ERR_FAILED, NULL);
|
| + return;
|
| + } else {
|
| + read_callback_ = callback;
|
| + }
|
| +
|
| + int result = net::ERR_FAILED;
|
| + scoped_refptr<net::IOBuffer> io_buffer;
|
| + do {
|
| + if (count < 0) {
|
| + result = net::ERR_INVALID_ARGUMENT;
|
| + break;
|
| + }
|
| +
|
| + if (ssl_state_ == SSLSTATE_CONNECTED) {
|
| + if (!ssl_socket_.get() || !ssl_socket_->IsConnected()) {
|
| + result = net::ERR_SOCKET_NOT_CONNECTED;
|
| + break;
|
| + }
|
| + DVLOG(2) << "Reading from SSL socket.";
|
| + io_buffer = new net::IOBuffer(count);
|
| + result = ssl_socket_->Read(io_buffer.get(), count,
|
| + base::Bind(&SSLSocket::OnReadComplete, base::Unretained(this),
|
| + io_buffer));
|
| + } else {
|
| + if (!socket_.get() || !socket_->IsConnected()) {
|
| + result = net::ERR_SOCKET_NOT_CONNECTED;
|
| + break;
|
| + }
|
| + DVLOG(2) << "Reading from normal socket.";
|
| + io_buffer = new net::IOBuffer(count);
|
| + result = socket_->Read(io_buffer.get(), count,
|
| + base::Bind(&SSLSocket::OnReadComplete, base::Unretained(this),
|
| + io_buffer));
|
| + }
|
| + } while (false);
|
| +
|
| + if (result != net::ERR_IO_PENDING)
|
| + OnReadComplete(io_buffer, result);
|
| +}
|
| +
|
| +void SSLSocket::RecvFrom(int count,
|
| + const RecvFromCompletionCallback& callback) {
|
| + callback.Run(net::ERR_FAILED, NULL, NULL, 0);
|
| +}
|
| +
|
| +void SSLSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
|
| + int byte_count,
|
| + const std::string& address,
|
| + int port,
|
| + const CompletionCallback& callback) {
|
| + callback.Run(net::ERR_FAILED);
|
| +}
|
| +
|
| +bool SSLSocket::SetKeepAlive(bool enable, int delay) {
|
| + if (!socket_.get())
|
| + return false;
|
| + return socket_->SetKeepAlive(enable, delay);
|
| +}
|
| +
|
| +bool SSLSocket::SetNoDelay(bool no_delay) {
|
| + if (!socket_.get())
|
| + return false;
|
| + return socket_->SetNoDelay(no_delay);
|
| +}
|
| +
|
| +int SSLSocket::WriteImpl(net::IOBuffer* io_buffer,
|
| + int io_buffer_size,
|
| + const net::CompletionCallback& callback) {
|
| + if (ssl_state_ == SSLSTATE_CONNECTED) {
|
| + if (!ssl_socket_.get() || !ssl_socket_->IsConnected())
|
| + return net::ERR_SOCKET_NOT_CONNECTED;
|
| + DVLOG(2) << "Writing " << io_buffer_size << "B to SSL socket.";
|
| + return ssl_socket_->Write(io_buffer, io_buffer_size, callback);
|
| + } else if (ssl_state_ == SSLSTATE_NONE) {
|
| + if (!socket_.get() || !socket_->IsConnected())
|
| + return net::ERR_SOCKET_NOT_CONNECTED;
|
| + DVLOG(2) << "Writing " << io_buffer_size << "B to normal socket.";
|
| + return socket_->Write(io_buffer, io_buffer_size, callback);
|
| + } else if (ssl_state_ == SSLSTATE_WAIT && socket_.get()) {
|
| + // Write dregs out to the insecure socket. We'll complete the handshake
|
| + // once the write queue empties.
|
| + if (!socket_.get() || !socket_->IsConnected())
|
| + return net::ERR_SOCKET_NOT_CONNECTED;
|
| + DVLOG(2) << "Writing " << io_buffer_size
|
| + << "B to normal socket (in SSLSTATE_WAIT).";
|
| + return socket_->Write(io_buffer, io_buffer_size, callback);
|
| + } else {
|
| + VLOG(1) << "Bad state for writing.";
|
| + return net::ERR_FAILED;
|
| + }
|
| +}
|
| +
|
| +void SSLSocket::OnConnectComplete(int result) {
|
| + DCHECK(!connect_callback_.is_null());
|
| + DCHECK(!is_connected_);
|
| + is_connected_ = result == net::OK;
|
| + connect_callback_.Run(result);
|
| + connect_callback_.Reset();
|
| +}
|
| +
|
| +void SSLSocket::OnHandshakeComplete(int result) {
|
| + DCHECK(!handshake_callback_.is_null());
|
| + DCHECK(ssl_state_ == SSLSTATE_WAIT);
|
| + ssl_state_ = result == net::OK ? SSLSTATE_CONNECTED : SSLSTATE_ERROR;
|
| + handshake_callback_.Run(result);
|
| + handshake_callback_.Reset();
|
| +}
|
| +
|
| +void SSLSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer,
|
| + int result) {
|
| + DCHECK(!read_callback_.is_null());
|
| + read_callback_.Run(result, io_buffer);
|
| + read_callback_.Reset();
|
| + DVLOG(2) << "OnReadComplete in state " << ssl_state_;
|
| + if (ssl_state_ == SSLSTATE_WAIT) {
|
| + AttemptSSLHandshake();
|
| + }
|
| +}
|
| +
|
| +void SSLSocket::OnWriteComplete(int result) {
|
| + Socket::OnWriteComplete(result);
|
| + DVLOG(2) << "OnWriteComplete in state " << ssl_state_;
|
| + if (ssl_state_ == SSLSTATE_WAIT) {
|
| + AttemptSSLHandshake();
|
| + }
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|