diff options
author | zork@chromium.org <zork@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-26 23:38:24 +0000 |
---|---|---|
committer | zork@chromium.org <zork@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-26 23:38:24 +0000 |
commit | 7afe339b8b7906587ce4cfd218df4cffae89e18f (patch) | |
tree | 51184ce93deb6a5950d95d7ac17f2070b9f6da8d /chrome/browser/sync/notifier | |
parent | 5fcc3f7533d627cfe55dc44349b0e7f6d242f133 (diff) | |
download | chromium_src-7afe339b8b7906587ce4cfd218df4cffae89e18f.zip chromium_src-7afe339b8b7906587ce4cfd218df4cffae89e18f.tar.gz chromium_src-7afe339b8b7906587ce4cfd218df4cffae89e18f.tar.bz2 |
Add SSL wrapper for linux and mac. This allows notifier to use chrome's SSL layer instead of OpenSSL.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/270074
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30123 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync/notifier')
3 files changed, 490 insertions, 1 deletions
diff --git a/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.cc b/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.cc new file mode 100755 index 0000000..28acf08 --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.cc @@ -0,0 +1,348 @@ +// Copyright (c) 2009 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/sync/notifier/communicator/ssl_socket_adapter.h" + +#include "base/compiler_specific.h" +#include "chrome/browser/net/url_request_context_getter.h" +#include "chrome/browser/profile.h" +#include "net/base/ssl_config_service.h" +#include "net/socket/client_socket_factory.h" +#include "net/url_request/url_request_context.h" + +namespace notifier { + +namespace { + +// Convert values from <errno.h> to values from "net/base/net_errors.h" +int MapPosixError(int err) { + // There are numerous posix error codes, but these are the ones we thus far + // find interesting. + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + return net::ERR_IO_PENDING; + case ENETDOWN: + return net::ERR_INTERNET_DISCONNECTED; + case ETIMEDOUT: + return net::ERR_TIMED_OUT; + case ECONNRESET: + case ENETRESET: // Related to keep-alive + return net::ERR_CONNECTION_RESET; + case ECONNABORTED: + return net::ERR_CONNECTION_ABORTED; + case ECONNREFUSED: + return net::ERR_CONNECTION_REFUSED; + case EHOSTUNREACH: + case ENETUNREACH: + return net::ERR_ADDRESS_UNREACHABLE; + case EADDRNOTAVAIL: + return net::ERR_ADDRESS_INVALID; + case 0: + return net::OK; + default: + LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; + return net::ERR_FAILED; + } +} + +} + +SSLSocketAdapter* SSLSocketAdapter::Create(AsyncSocket* socket) { + return new SSLSocketAdapter(socket); +} + +SSLSocketAdapter::SSLSocketAdapter(AsyncSocket* socket) + : AsyncSocketAdapter(socket), + ignore_bad_cert_(false), + ALLOW_THIS_IN_INITIALIZER_LIST( + connected_callback_(this, &SSLSocketAdapter::OnConnected)), + ALLOW_THIS_IN_INITIALIZER_LIST( + io_callback_(this, &SSLSocketAdapter::OnIO)), + ssl_connected_(false), + state_(STATE_NONE) { + socket_.reset(new TransportSocket(socket, this)); +} + +int SSLSocketAdapter::StartSSL(const char* hostname, bool restartable) { + DCHECK(!restartable); + + // SSLConfigService is not thread-safe, and the default values for SSLConfig + // are correct for us, so we don't use the config service to initialize this + // object. + net::SSLConfig ssl_config; + socket_->set_addr(talk_base::SocketAddress(hostname)); + ssl_socket_.reset( + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + socket_.release(), hostname, ssl_config)); + + int result = ssl_socket_->Connect(&connected_callback_); + + if (result == net::ERR_IO_PENDING || result == net::OK) { + return 0; + } else { + return result; + } +} + +int SSLSocketAdapter::Send(const void* buf, size_t len) { + if (!ssl_connected_) { + return AsyncSocketAdapter::Send(buf, len); + } + + switch (state_) { + case STATE_NONE: { + transport_buf_ = new net::IOBuffer(len); + memcpy(transport_buf_->data(), buf, len); + + int result = ssl_socket_->Write(transport_buf_, len, &io_callback_); + if (result == net::ERR_IO_PENDING) { + state_ = STATE_WRITE; + SetError(EWOULDBLOCK); + } else { + transport_buf_ = NULL; + } + return result; + } + case STATE_WRITE_COMPLETE: + transport_buf_ = NULL; + state_ = STATE_NONE; + return data_transferred_; + + case STATE_READ: + case STATE_READ_COMPLETE: + case STATE_WRITE: + SetError(EWOULDBLOCK); + return -1; + + default: + NOTREACHED(); + break; + } + return -1; +} + +int SSLSocketAdapter::Recv(void* buf, size_t len) { + if (!ssl_connected_) { + return AsyncSocketAdapter::Recv(buf, len); + } + + switch (state_) { + case STATE_NONE: { + transport_buf_ = new net::IOBuffer(len); + int result = ssl_socket_->Read(transport_buf_, len, &io_callback_); + if (result >= 0) { + memcpy(buf, transport_buf_->data(), len); + } + + if (result == net::ERR_IO_PENDING) { + state_ = STATE_READ; + SetError(EWOULDBLOCK); + } else { + transport_buf_ = NULL; + } + return result; + } + case STATE_READ_COMPLETE: + memcpy(buf, transport_buf_->data(), len); + transport_buf_ = NULL; + state_ = STATE_NONE; + return data_transferred_; + + case STATE_READ: + case STATE_WRITE: + case STATE_WRITE_COMPLETE: + SetError(EWOULDBLOCK); + return -1; + + default: + NOTREACHED(); + break; + } + return -1; +} + +void SSLSocketAdapter::OnConnected(int result) { + if (result == net::OK) { + ssl_connected_ = true; + OnConnectEvent(this); + } +} + +void SSLSocketAdapter::OnIO(int result) { + switch (state_) { + case STATE_READ: + state_ = STATE_READ_COMPLETE; + data_transferred_ = result; + break; + case STATE_WRITE: + state_ = STATE_WRITE_COMPLETE; + data_transferred_ = result; + break; + case STATE_NONE: + case STATE_READ_COMPLETE: + case STATE_WRITE_COMPLETE: + default: + NOTREACHED(); + break; + } +} + +TransportSocket::TransportSocket(talk_base::AsyncSocket* socket, + SSLSocketAdapter *ssl_adapter) + : connect_callback_(NULL), + read_callback_(NULL), + write_callback_(NULL), + read_buffer_len_(0), + write_buffer_len_(0), + socket_(socket), + ssl_adapter_(ssl_adapter) { + socket_->SignalConnectEvent.connect(this, &TransportSocket::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &TransportSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &TransportSocket::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &TransportSocket::OnCloseEvent); +} + +int TransportSocket::Connect(net::CompletionCallback* callback) { + connect_callback_ = callback; + return socket_->Connect(addr_); +} + +void TransportSocket::Disconnect() { + socket_->Close(); +} + +bool TransportSocket::IsConnected() const { + return (socket_->GetState() == talk_base::Socket::CS_CONNECTED); +} + +bool TransportSocket::IsConnectedAndIdle() const { + // Not implemented. + NOTREACHED(); + return false; +} + +#if defined(OS_LINUX) || defined(OS_MACOSX) +int TransportSocket::GetPeerName(struct sockaddr *name, socklen_t *namelen) { + talk_base::SocketAddress address = socket_->GetRemoteAddress(); + address.ToSockAddr(reinterpret_cast<sockaddr_in *>(name)); + return 0; +} +#endif + +int TransportSocket::Read(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback) { + DCHECK(buf); + DCHECK(!read_callback_); + DCHECK(!read_buffer_.get()); + int result = socket_->Recv(buf->data(), buf_len); + if (result < 0) { + result = MapPosixError(socket_->GetError()); + if (result == net::ERR_IO_PENDING) { + read_callback_ = callback; + read_buffer_ = buf; + read_buffer_len_ = buf_len; + } + } + return result; +} + +int TransportSocket::Write(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback) { + DCHECK(buf); + DCHECK(!write_callback_); + DCHECK(!write_buffer_.get()); + int result = socket_->Send(buf->data(), buf_len); + if (result < 0) { + result = MapPosixError(socket_->GetError()); + if (result == net::ERR_IO_PENDING) { + write_callback_ = callback; + write_buffer_ = buf; + write_buffer_len_ = buf_len; + } + } + return result; +} + +bool TransportSocket::SetReceiveBufferSize(int32 size) { + // Not implemented. + return false; +} + +bool TransportSocket::SetSendBufferSize(int32 size) { + // Not implemented. + return false; +} + +void TransportSocket::OnConnectEvent(talk_base::AsyncSocket * socket) { + if (connect_callback_) { + net::CompletionCallback *callback = connect_callback_; + connect_callback_ = NULL; + callback->RunWithParams(Tuple1<int>(MapPosixError(socket_->GetError()))); + } else { + ssl_adapter_->OnConnectEvent(socket); + } +} + +void TransportSocket::OnReadEvent(talk_base::AsyncSocket* socket) { + if (read_callback_) { + DCHECK(read_buffer_.get()); + net::CompletionCallback* callback = read_callback_; + scoped_refptr<net::IOBuffer> buffer = read_buffer_; + int buffer_len = read_buffer_len_; + + read_callback_ = NULL; + read_buffer_ = NULL; + read_buffer_len_ = 0; + + int result = socket_->Recv(buffer->data(), buffer_len); + if (result < 0) { + result = MapPosixError(socket_->GetError()); + if (result == net::ERR_IO_PENDING) { + read_callback_ = callback; + read_buffer_ = buffer; + read_buffer_len_ = buffer_len; + return; + } + } + callback->RunWithParams(Tuple1<int>(result)); + } else { + ssl_adapter_->OnReadEvent(socket); + } +} + +void TransportSocket::OnWriteEvent(talk_base::AsyncSocket* socket) { + if (write_callback_) { + DCHECK(write_buffer_.get()); + net::CompletionCallback* callback = write_callback_; + scoped_refptr<net::IOBuffer> buffer = write_buffer_; + int buffer_len = write_buffer_len_; + + write_callback_ = NULL; + write_buffer_ = NULL; + write_buffer_len_ = 0; + + int result = socket_->Send(buffer->data(), buffer_len); + if (result < 0) { + result = MapPosixError(socket_->GetError()); + if (result == net::ERR_IO_PENDING) { + write_callback_ = callback; + write_buffer_ = buffer; + write_buffer_len_ = buffer_len; + return; + } + } + callback->RunWithParams(Tuple1<int>(result)); + } else { + ssl_adapter_->OnWriteEvent(socket); + } +} + +void TransportSocket::OnCloseEvent(talk_base::AsyncSocket* socket, int err) { + ssl_adapter_->OnCloseEvent(socket, err); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.h b/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.h new file mode 100755 index 0000000..f121e1b --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/ssl_socket_adapter.h @@ -0,0 +1,127 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_ + +#include "base/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/socket/client_socket.h" +#include "net/socket/ssl_client_socket.h" +#include "talk/base/asyncsocket.h" + +namespace notifier { + +class SSLSocketAdapter; + +// This class provides a wrapper to libjingle's talk_base::AsyncSocket that +// implements Chromium's net::ClientSocket interface. It's used by +// SSLSocketAdapter to enable Chromium's SSL implementation to work over +// libjingle's socket class. +class TransportSocket : public net::ClientSocket, public sigslot::has_slots<> { + public: + TransportSocket(talk_base::AsyncSocket* socket, + SSLSocketAdapter *ssl_adapter); + + void set_addr(const talk_base::SocketAddress& addr) { + addr_ = addr; + } + + // net::ClientSocket implementation + + virtual int Connect(net::CompletionCallback* callback); + virtual void Disconnect(); + virtual bool IsConnected() const; + virtual bool IsConnectedAndIdle() const; + +#if defined(OS_LINUX) || defined(OS_MACOSX) + virtual int GetPeerName(struct sockaddr *name, socklen_t *namelen); +#endif + + // net::Socket 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); + + private: + void OnConnectEvent(talk_base::AsyncSocket * socket); + void OnReadEvent(talk_base::AsyncSocket * socket); + void OnWriteEvent(talk_base::AsyncSocket * socket); + void OnCloseEvent(talk_base::AsyncSocket * socket, int err); + + net::CompletionCallback* connect_callback_; + net::CompletionCallback* read_callback_; + net::CompletionCallback* write_callback_; + + scoped_refptr<net::IOBuffer> read_buffer_; + int read_buffer_len_; + scoped_refptr<net::IOBuffer> write_buffer_; + int write_buffer_len_; + + talk_base::AsyncSocket *socket_; + talk_base::SocketAddress addr_; + SSLSocketAdapter *ssl_adapter_; + + DISALLOW_COPY_AND_ASSIGN(TransportSocket); +}; + +// This provides a talk_base::AsyncSocketAdapter interface around Chromium's +// net::SSLClientSocket class. This allows notifier to use Chromium's SSL +// implementation instead of OpenSSL. +class SSLSocketAdapter : public talk_base::AsyncSocketAdapter { + public: + explicit SSLSocketAdapter(talk_base::AsyncSocket* socket); + + bool ignore_bad_cert() const { return ignore_bad_cert_; } + void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; } + + // StartSSL returns 0 if successful, or non-zero on failure. + // If StartSSL is called while the socket is closed or connecting, the SSL + // negotiation will begin as soon as the socket connects. + // + // restartable is not implemented, and must be set to false. + virtual int StartSSL(const char* hostname, bool restartable); + + // Create the default SSL adapter for this platform. + static SSLSocketAdapter* Create(AsyncSocket* socket); + + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + + private: + friend class TransportSocket; + + enum State { + STATE_NONE, + STATE_READ, + STATE_READ_COMPLETE, + STATE_WRITE, + STATE_WRITE_COMPLETE + }; + + void OnConnected(int result); + void OnIO(int result); + + bool ignore_bad_cert_; + scoped_ptr<TransportSocket> socket_; + scoped_ptr<net::SSLClientSocket> ssl_socket_; + net::CompletionCallbackImpl<SSLSocketAdapter> connected_callback_; + net::CompletionCallbackImpl<SSLSocketAdapter> io_callback_; + bool ssl_connected_; + State state_; + scoped_refptr<net::IOBuffer> transport_buf_; + int data_transferred_; + + DISALLOW_COPY_AND_ASSIGN(SSLSocketAdapter); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_SSL_SOCKET_ADAPTER_H_ diff --git a/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc index 03c1977..b7a3602 100644 --- a/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc +++ b/chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc @@ -9,12 +9,17 @@ #include "base/logging.h" #include "chrome/browser/sync/notifier/communicator/product_info.h" +#if defined(OS_LINUX) || defined(OS_MACOSX) +#include "chrome/browser/sync/notifier/communicator/ssl_socket_adapter.h" +#endif #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/firewallsocketserver.h" #include "talk/base/logging.h" #include "talk/base/socketadapters.h" +#if defined(OS_WIN) #include "talk/base/ssladapter.h" +#endif #include "talk/xmpp/xmppengine.h" namespace notifier { @@ -132,7 +137,11 @@ bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) { } #if defined(FEATURE_ENABLE_SSL) +#if defined(OS_WIN) talk_base::SSLAdapter* ssl = talk_base::SSLAdapter::Create(socket); +#else + notifier::SSLSocketAdapter* ssl = notifier::SSLSocketAdapter::Create(socket); +#endif socket = ssl; #endif @@ -320,8 +329,13 @@ bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) { ASSERT(write_buffer_length_ == 0); +#if defined(OS_WIN) talk_base::SSLAdapter* ssl_adapter = - static_cast<talk_base::SSLAdapter*>(socket_); + static_cast<talk_base::SSLAdapter*>(socket_); +#else + notifier::SSLSocketAdapter* ssl_adapter = + static_cast<notifier::SSLSocketAdapter*>(socket_); +#endif if (allow_unverified_certs_) { ssl_adapter->set_ignore_bad_cert(true); |