Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1569)

Unified Diff: chrome/browser/extensions/api/socket/ssl_socket.cc

Issue 10580028: Experimental support for SSL in platform apps sockets. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/extensions/api/socket/ssl_socket.h ('k') | chrome/chrome_browser_extensions.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « chrome/browser/extensions/api/socket/ssl_socket.h ('k') | chrome/chrome_browser_extensions.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698