diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-10 20:03:15 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-10 20:03:15 +0000 |
commit | f73e174dbd1fcd3ef9ca23dbe0a4e789068ee020 (patch) | |
tree | a6f7446f28a742d9b190fd78ee8276245d3bca8d /remoting/jingle_glue | |
parent | 833d617cffb3de3e90638d963a73aa7158b6843a (diff) | |
download | chromium_src-f73e174dbd1fcd3ef9ca23dbe0a4e789068ee020.zip chromium_src-f73e174dbd1fcd3ef9ca23dbe0a4e789068ee020.tar.gz chromium_src-f73e174dbd1fcd3ef9ca23dbe0a4e789068ee020.tar.bz2 |
Removed use of XmppSocketAdapter by sync.
Moved XmppSocketAdapter and friends to remoting directory.
Removed some dead code in jingle/.
This is a relanding of 59012, which was mistakenly reverted at 59104.
BUG=54146,55116
TEST=none
TBR=willchan
Review URL: http://codereview.chromium.org/3300030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59134 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/jingle_glue')
-rw-r--r-- | remoting/jingle_glue/jingle_client.cc | 4 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_adapter.cc | 27 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_adapter.h | 33 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_socket_adapter.cc | 386 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_socket_adapter.h | 145 | ||||
-rw-r--r-- | remoting/jingle_glue/xmpp_socket_adapter.cc | 428 | ||||
-rw-r--r-- | remoting/jingle_glue/xmpp_socket_adapter.h | 87 |
7 files changed, 1108 insertions, 2 deletions
diff --git a/remoting/jingle_glue/jingle_client.cc b/remoting/jingle_glue/jingle_client.cc index 01e607b..d3ca67e 100644 --- a/remoting/jingle_glue/jingle_client.cc +++ b/remoting/jingle_glue/jingle_client.cc @@ -7,10 +7,10 @@ #include "base/logging.h" #include "base/message_loop.h" #include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h" -#include "jingle/notifier/communicator/xmpp_socket_adapter.h" #include "remoting/jingle_glue/iq_request.h" #include "remoting/jingle_glue/jingle_thread.h" #include "remoting/jingle_glue/relay_port_allocator.h" +#include "remoting/jingle_glue/xmpp_socket_adapter.h" #include "third_party/libjingle/source/talk/base/asyncsocket.h" #include "third_party/libjingle/source/talk/base/ssladapter.h" #include "third_party/libjingle/source/talk/p2p/base/sessionmanager.h" @@ -94,7 +94,7 @@ void JingleClient::DoInitialize(const std::string& username, client_->SignalStateChange.connect( this, &JingleClient::OnConnectionStateChanged); - buzz::AsyncSocket* socket = new notifier::XmppSocketAdapter(settings, false); + buzz::AsyncSocket* socket = new XmppSocketAdapter(settings, false); client_->Connect(settings, "", socket, CreatePreXmppAuth(settings)); client_->Start(); diff --git a/remoting/jingle_glue/ssl_adapter.cc b/remoting/jingle_glue/ssl_adapter.cc new file mode 100644 index 0000000..68d350c --- /dev/null +++ b/remoting/jingle_glue/ssl_adapter.cc @@ -0,0 +1,27 @@ +// 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 "remoting/jingle_glue/ssl_adapter.h" + +#if defined(OS_WIN) +#include "talk/base/ssladapter.h" +#else +#include "remoting/jingle_glue/ssl_socket_adapter.h" +#endif + +namespace remoting { + +talk_base::SSLAdapter* CreateSSLAdapter(talk_base::AsyncSocket* socket) { + talk_base::SSLAdapter* ssl_adapter = +#if defined(OS_WIN) + talk_base::SSLAdapter::Create(socket); +#else + remoting::SSLSocketAdapter::Create(socket); +#endif + DCHECK(ssl_adapter); + return ssl_adapter; +} + +} // namespace remoting + diff --git a/remoting/jingle_glue/ssl_adapter.h b/remoting/jingle_glue/ssl_adapter.h new file mode 100644 index 0000000..56b03be --- /dev/null +++ b/remoting/jingle_glue/ssl_adapter.h @@ -0,0 +1,33 @@ +// 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 REMOTING_JINGLE_GLUE_SSL_ADAPTER_H_ +#define REMOTING_JINGLE_GLUE_SSL_ADAPTER_H_ + +namespace talk_base { +class AsyncSocket; +class SSLAdapter; +} // namespace talk_base + +namespace remoting { + +// Wraps the given socket in a platform-dependent SSLAdapter +// implementation. +talk_base::SSLAdapter* CreateSSLAdapter(talk_base::AsyncSocket* socket); + +// Utility template class that overrides CreateSSLAdapter() to use the +// above function. +template <class SocketFactory> +class SSLAdapterSocketFactory : public SocketFactory { + public: + virtual talk_base::SSLAdapter* CreateSSLAdapter( + talk_base::AsyncSocket* socket) { + return ::remoting::CreateSSLAdapter(socket); + } +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_SSL_ADAPTER_H_ + diff --git a/remoting/jingle_glue/ssl_socket_adapter.cc b/remoting/jingle_glue/ssl_socket_adapter.cc new file mode 100644 index 0000000..3ea404b --- /dev/null +++ b/remoting/jingle_glue/ssl_socket_adapter.cc @@ -0,0 +1,386 @@ +// 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 "remoting/jingle_glue/ssl_socket_adapter.h" + +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "net/base/address_list.h" +#include "net/base/net_errors.h" +#include "net/base/ssl_config_service.h" +#include "net/base/sys_addrinfo.h" +#include "net/socket/client_socket_factory.h" +#include "net/url_request/url_request_context.h" + +namespace remoting { + +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; + } +} + +} // namespace + +SSLSocketAdapter* SSLSocketAdapter::Create(AsyncSocket* socket) { + return new SSLSocketAdapter(socket); +} + +SSLSocketAdapter::SSLSocketAdapter(AsyncSocket* socket) + : SSLAdapter(socket), + ignore_bad_cert_(false), + ALLOW_THIS_IN_INITIALIZER_LIST( + connected_callback_(this, &SSLSocketAdapter::OnConnected)), + ALLOW_THIS_IN_INITIALIZER_LIST( + read_callback_(this, &SSLSocketAdapter::OnRead)), + ALLOW_THIS_IN_INITIALIZER_LIST( + write_callback_(this, &SSLSocketAdapter::OnWrite)), + ssl_state_(SSLSTATE_NONE), + read_state_(IOSTATE_NONE), + write_state_(IOSTATE_NONE) { + transport_socket_ = new TransportSocket(socket, this); +} + +SSLSocketAdapter::~SSLSocketAdapter() { +} + +int SSLSocketAdapter::StartSSL(const char* hostname, bool restartable) { + DCHECK(!restartable); + hostname_ = hostname; + + if (socket_->GetState() != Socket::CS_CONNECTED) { + ssl_state_ = SSLSTATE_WAIT; + return 0; + } else { + return BeginSSL(); + } +} + +int SSLSocketAdapter::BeginSSL() { + if (!MessageLoop::current()) { + // Certificate verification is done via the Chrome message loop. + // Without this check, if we don't have a chrome message loop the + // SSL connection just hangs silently. + LOG(DFATAL) << "Chrome message loop (needed by SSL certificate " + << "verification) does not exist"; + return net::ERR_UNEXPECTED; + } + + // 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; + transport_socket_->set_addr(talk_base::SocketAddress(hostname_, 0)); + ssl_socket_.reset( + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + transport_socket_, hostname_.c_str(), ssl_config)); + + int result = ssl_socket_->Connect(&connected_callback_); + + if (result == net::ERR_IO_PENDING || result == net::OK) { + return 0; + } else { + LOG(ERROR) << "Could not start SSL: " << net::ErrorToString(result); + return result; + } +} + +int SSLSocketAdapter::Send(const void* buf, size_t len) { + if (ssl_state_ != SSLSTATE_CONNECTED) { + return AsyncSocketAdapter::Send(buf, len); + } else { + scoped_refptr<net::IOBuffer> transport_buf = new net::IOBuffer(len); + memcpy(transport_buf->data(), buf, len); + + int result = ssl_socket_->Write(transport_buf, len, NULL); + if (result == net::ERR_IO_PENDING) { + SetError(EWOULDBLOCK); + } + transport_buf = NULL; + return result; + } +} + +int SSLSocketAdapter::Recv(void* buf, size_t len) { + switch (ssl_state_) { + case SSLSTATE_NONE: + return AsyncSocketAdapter::Recv(buf, len); + + case SSLSTATE_WAIT: + SetError(EWOULDBLOCK); + return -1; + + case SSLSTATE_CONNECTED: + switch (read_state_) { + case IOSTATE_NONE: { + transport_buf_ = new net::IOBuffer(len); + int result = ssl_socket_->Read(transport_buf_, len, &read_callback_); + if (result >= 0) { + memcpy(buf, transport_buf_->data(), len); + } + + if (result == net::ERR_IO_PENDING) { + read_state_ = IOSTATE_PENDING; + SetError(EWOULDBLOCK); + } else { + if (result < 0) { + SetError(result); + LOG(INFO) << "Socket error " << result; + } + transport_buf_ = NULL; + } + return result; + } + case IOSTATE_PENDING: + SetError(EWOULDBLOCK); + return -1; + + case IOSTATE_COMPLETE: + memcpy(buf, transport_buf_->data(), len); + transport_buf_ = NULL; + read_state_ = IOSTATE_NONE; + return data_transferred_; + } + } + + NOTREACHED(); + return -1; +} + +void SSLSocketAdapter::OnConnected(int result) { + if (result == net::OK) { + ssl_state_ = SSLSTATE_CONNECTED; + OnConnectEvent(this); + } else { + LOG(WARNING) << "OnConnected failed with error " << result; + } +} + +void SSLSocketAdapter::OnRead(int result) { + DCHECK(read_state_ == IOSTATE_PENDING); + read_state_ = IOSTATE_COMPLETE; + data_transferred_ = result; + AsyncSocketAdapter::OnReadEvent(this); +} + +void SSLSocketAdapter::OnWrite(int result) { + DCHECK(write_state_ == IOSTATE_PENDING); + write_state_ = IOSTATE_COMPLETE; + data_transferred_ = result; + AsyncSocketAdapter::OnWriteEvent(this); +} + +void SSLSocketAdapter::OnConnectEvent(talk_base::AsyncSocket* socket) { + if (ssl_state_ != SSLSTATE_WAIT) { + AsyncSocketAdapter::OnConnectEvent(socket); + } else { + ssl_state_ = SSLSTATE_NONE; + int result = BeginSSL(); + if (0 != result) { + // TODO(zork): Handle this case gracefully. + LOG(WARNING) << "BeginSSL() failed with " << result; + } + } +} + +TransportSocket::TransportSocket(talk_base::AsyncSocket* socket, + SSLSocketAdapter *ssl_adapter) + : read_callback_(NULL), + write_callback_(NULL), + read_buffer_len_(0), + write_buffer_len_(0), + socket_(socket), + was_used_to_convey_data_(false) { + socket_->SignalReadEvent.connect(this, &TransportSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &TransportSocket::OnWriteEvent); +} + +TransportSocket::~TransportSocket() { +} + +int TransportSocket::Connect(net::CompletionCallback* callback) { + // Connect is never called by SSLClientSocket, instead SSLSocketAdapter + // calls Connect() on socket_ directly. + NOTREACHED(); + return false; +} + +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; +} + +int TransportSocket::GetPeerAddress(net::AddressList* address) const { + talk_base::SocketAddress socket_address = socket_->GetRemoteAddress(); + + // libjingle supports only IPv4 addresses. + sockaddr_in ipv4addr; + socket_address.ToSockAddr(&ipv4addr); + + struct addrinfo ai; + memset(&ai, 0, sizeof(ai)); + ai.ai_family = ipv4addr.sin_family; + ai.ai_socktype = SOCK_STREAM; + ai.ai_protocol = IPPROTO_TCP; + ai.ai_addr = reinterpret_cast<struct sockaddr*>(&ipv4addr); + ai.ai_addrlen = sizeof(ipv4addr); + + address->Copy(&ai, false); + return net::OK; +} + +void TransportSocket::SetSubresourceSpeculation() { + NOTREACHED(); +} + +void TransportSocket::SetOmniboxSpeculation() { + NOTREACHED(); +} + +bool TransportSocket::WasEverUsed() const { + // We don't use this in ClientSocketPools, so this should never be used. + NOTREACHED(); + return was_used_to_convey_data_; +} + +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; + } + } + if (result != net::ERR_IO_PENDING) + was_used_to_convey_data_ = true; + 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; + } + } + if (result != net::ERR_IO_PENDING) + was_used_to_convey_data_ = true; + return result; +} + +bool TransportSocket::SetReceiveBufferSize(int32 size) { + // Not implemented. + return false; +} + +bool TransportSocket::SetSendBufferSize(int32 size) { + // Not implemented. + return false; +} + +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; + } + } + was_used_to_convey_data_ = true; + callback->RunWithParams(Tuple1<int>(result)); + } +} + +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; + } + } + was_used_to_convey_data_ = true; + callback->RunWithParams(Tuple1<int>(result)); + } +} + +} // namespace remoting diff --git a/remoting/jingle_glue/ssl_socket_adapter.h b/remoting/jingle_glue/ssl_socket_adapter.h new file mode 100644 index 0000000..a22e2b7 --- /dev/null +++ b/remoting/jingle_glue/ssl_socket_adapter.h @@ -0,0 +1,145 @@ +// 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. + +#ifndef REMOTING_JINGLE_GLUE_SSL_SOCKET_ADAPTER_H_ +#define REMOTING_JINGLE_GLUE_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/base/net_log.h" +#include "net/socket/client_socket.h" +#include "net/socket/ssl_client_socket.h" +#include "talk/base/asyncsocket.h" +#include "talk/base/ssladapter.h" + +namespace remoting { + +class SSLSocketAdapter; + +// TODO(sergeyu): Write unittests for this code! + +// 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); + ~TransportSocket(); + + 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; + virtual int GetPeerAddress(net::AddressList* address) const; + virtual const net::BoundNetLog& NetLog() const { return net_log_; } + virtual void SetSubresourceSpeculation(); + virtual void SetOmniboxSpeculation(); + virtual bool WasEverUsed() const; + + // 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: + friend class SSLSocketAdapter; + + void OnReadEvent(talk_base::AsyncSocket* socket); + void OnWriteEvent(talk_base::AsyncSocket* socket); + + 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_; + + net::BoundNetLog net_log_; + + talk_base::AsyncSocket *socket_; + talk_base::SocketAddress addr_; + + bool was_used_to_convey_data_; + + DISALLOW_COPY_AND_ASSIGN(TransportSocket); +}; + +// This provides a talk_base::AsyncSocketAdapter interface around Chromium's +// net::SSLClientSocket class. This allows remoting to use Chromium's SSL +// implementation instead of OpenSSL. +class SSLSocketAdapter : public talk_base::SSLAdapter { + public: + explicit SSLSocketAdapter(talk_base::AsyncSocket* socket); + ~SSLSocketAdapter(); + + // 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 SSLState { + SSLSTATE_NONE, + SSLSTATE_WAIT, + SSLSTATE_CONNECTED, + }; + + enum IOState { + IOSTATE_NONE, + IOSTATE_PENDING, + IOSTATE_COMPLETE, + }; + + void OnConnected(int result); + void OnRead(int result); + void OnWrite(int result); + + virtual void OnConnectEvent(talk_base::AsyncSocket* socket); + + int BeginSSL(); + + bool ignore_bad_cert_; + std::string hostname_; + TransportSocket* transport_socket_; + scoped_ptr<net::SSLClientSocket> ssl_socket_; + net::CompletionCallbackImpl<SSLSocketAdapter> connected_callback_; + net::CompletionCallbackImpl<SSLSocketAdapter> read_callback_; + net::CompletionCallbackImpl<SSLSocketAdapter> write_callback_; + SSLState ssl_state_; + IOState read_state_; + IOState write_state_; + scoped_refptr<net::IOBuffer> transport_buf_; + int data_transferred_; + + DISALLOW_COPY_AND_ASSIGN(SSLSocketAdapter); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_SSL_SOCKET_ADAPTER_H_ diff --git a/remoting/jingle_glue/xmpp_socket_adapter.cc b/remoting/jingle_glue/xmpp_socket_adapter.cc new file mode 100644 index 0000000..08f72aa --- /dev/null +++ b/remoting/jingle_glue/xmpp_socket_adapter.cc @@ -0,0 +1,428 @@ +// 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 "remoting/jingle_glue/xmpp_socket_adapter.h" + +#include <iomanip> +#include <string> + +#include "base/logging.h" +#include "remoting/jingle_glue/ssl_adapter.h" +#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" +#include "talk/base/ssladapter.h" +#include "talk/base/thread.h" +#include "talk/xmpp/xmppengine.h" + +namespace remoting { + +XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs, + bool allow_unverified_certs) + : state_(STATE_CLOSED), + error_(ERROR_NONE), + wsa_error_(0), + socket_(NULL), + protocol_(xcs.protocol()), + firewall_(false), + write_buffer_(NULL), + write_buffer_length_(0), + write_buffer_capacity_(0), + allow_unverified_certs_(allow_unverified_certs) { + proxy_.type = xcs.proxy(); + proxy_.address.SetIP(xcs.proxy_host()); + proxy_.address.SetPort(xcs.proxy_port()); + proxy_.username = xcs.proxy_user(); + proxy_.password = xcs.proxy_pass(); +} + +XmppSocketAdapter::~XmppSocketAdapter() { + FreeState(); + + // Clean up any previous socket - cannot delete socket on close because close + // happens during the child socket's stack callback. + if (socket_) { + delete socket_; + socket_ = NULL; + } +} + +bool XmppSocketAdapter::FreeState() { + int code = 0; + + // Clean up the socket. + if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) { + code = socket_->Close(); + } + + delete[] write_buffer_; + write_buffer_ = NULL; + write_buffer_length_ = 0; + write_buffer_capacity_ = 0; + + if (code) { + SetWSAError(code); + return false; + } + return true; +} + +bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) { + if (state_ != STATE_CLOSED) { + SetError(ERROR_WRONGSTATE); + return false; + } + + LOG(INFO) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")"; + + // Clean up any previous socket - cannot delete socket on close because close + // happens during the child socket's stack callback. + if (socket_) { + delete socket_; + socket_ = NULL; + } + + talk_base::AsyncSocket* socket = + talk_base::Thread::Current()->socketserver()->CreateAsyncSocket( + SOCK_STREAM); + if (!socket) { + SetWSAError(WSA_NOT_ENOUGH_MEMORY); + return false; + } + + if (firewall_) { + // TODO(sync): Change this to make WSAAsyncSockets support current thread + // socket server. + talk_base::FirewallSocketServer* fw = + static_cast<talk_base::FirewallSocketServer*>( + talk_base::Thread::Current()->socketserver()); + socket = fw->WrapSocket(socket, SOCK_STREAM); + } + + if (proxy_.type) { + talk_base::AsyncSocket* proxy_socket = 0; + if (proxy_.type == talk_base::PROXY_SOCKS5) { + proxy_socket = new talk_base::AsyncSocksProxySocket( + socket, proxy_.address, proxy_.username, proxy_.password); + } else { + // Note: we are trying unknown proxies as HTTPS currently. + proxy_socket = new talk_base::AsyncHttpsProxySocket(socket, + "chromoting", proxy_.address, proxy_.username, + proxy_.password); + } + if (!proxy_socket) { + SetWSAError(WSA_NOT_ENOUGH_MEMORY); + delete socket; + return false; + } + socket = proxy_socket; // For our purposes the proxy is now the socket. + } + + if (protocol_ == cricket::PROTO_SSLTCP) { + talk_base::AsyncSocket *fake_ssl_socket = + new talk_base::AsyncSSLSocket(socket); + if (!fake_ssl_socket) { + SetWSAError(WSA_NOT_ENOUGH_MEMORY); + delete socket; + return false; + } + socket = fake_ssl_socket; // For our purposes the SSL socket is the socket. + } + +#if defined(FEATURE_ENABLE_SSL) + talk_base::SSLAdapter* ssl_adapter = remoting::CreateSSLAdapter(socket); + socket = ssl_adapter; // For our purposes the SSL adapter is the socket. +#endif + + socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent); + socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent); + socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent); + socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent); + + // The linux implementation of socket::Connect returns an error when the + // connect didn't complete yet. This can be distinguished from a failure + // because socket::IsBlocking is true. Perhaps, the linux implementation + // should be made to behave like the windows version which doesn't do this, + // but it seems to be a pattern with these methods that they return an error + // if the operation didn't complete in a sync fashion and one has to check + // IsBlocking to tell if was a "real" error. + if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) { + SetWSAError(socket->GetError()); + delete socket; + return false; + } + + socket_ = socket; + state_ = STATE_CONNECTING; + return true; +} + +bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) { + if (len_read) + *len_read = 0; + + if (state_ <= STATE_CLOSING) { + SetError(ERROR_WRONGSTATE); + return false; + } + + DCHECK(socket_); + + if (IsOpen()) { + int result = socket_->Recv(data, len); + if (result < 0) { + if (!socket_->IsBlocking()) { + SetWSAError(socket_->GetError()); + return false; + } + + result = 0; + } + + if (len_read) + *len_read = result; + } + + return true; +} + +bool XmppSocketAdapter::Write(const char* data, size_t len) { + if (state_ <= STATE_CLOSING) { + // There may be data in a buffer that gets lost. Too bad! + SetError(ERROR_WRONGSTATE); + return false; + } + + DCHECK(socket_); + + size_t sent = 0; + + // Try an immediate write when there is no buffer and we aren't in SSL mode + // or opening the connection. + if (write_buffer_length_ == 0 && IsOpen()) { + int result = socket_->Send(data, len); + if (result < 0) { + if (!socket_->IsBlocking()) { + SetWSAError(socket_->GetError()); + return false; + } + result = 0; + } + + sent = static_cast<size_t>(result); + } + + // Buffer what we didn't send. + if (sent < len) { + QueueWriteData(data + sent, len - sent); + } + + // Service the socket right away to push the written data out in SSL mode. + return HandleWritable(); +} + +bool XmppSocketAdapter::Close() { + if (state_ == STATE_CLOSING) { + return false; // Avoid recursion, but not unexpected. + } + if (state_ == STATE_CLOSED) { + // In theory should not be trying to re-InternalClose. + SetError(ERROR_WRONGSTATE); + return false; + } + + // TODO(sync): deal with flushing close (flush, don't do reads, clean ssl). + + // If we've gotten to the point where we really do have a socket underneath + // then close it. It should call us back to tell us it is closed, and + // NotifyClose will be called. We indicate "closing" state so that we + // do not recusively try to keep closing the socket. + if (socket_) { + state_ = STATE_CLOSING; + socket_->Close(); + } + + // If we didn't get the callback, then we better make sure we signal + // closed. + if (state_ != STATE_CLOSED) { + // The socket was closed manually, not directly due to error. + if (error_ != ERROR_NONE) { + LOG(INFO) << "XmppSocketAdapter::Close - previous Error: " << error_ + << " WSAError: " << wsa_error_; + error_ = ERROR_NONE; + wsa_error_ = 0; + } + NotifyClose(); + } + return true; +} + +void XmppSocketAdapter::NotifyClose() { + if (state_ == STATE_CLOSED) { + SetError(ERROR_WRONGSTATE); + } else { + LOG(INFO) << "XmppSocketAdapter::NotifyClose - Error: " << error_ + << " WSAError: " << wsa_error_; + state_ = STATE_CLOSED; + SignalClosed(); + FreeState(); + } +} + +void XmppSocketAdapter::OnConnectEvent(talk_base::AsyncSocket *socket) { + if (state_ == STATE_CONNECTING) { + state_ = STATE_OPEN; + LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN"; + SignalConnected(); +#if defined(FEATURE_ENABLE_SSL) + } else if (state_ == STATE_TLS_CONNECTING) { + state_ = STATE_TLS_OPEN; + LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN"; + SignalSSLConnected(); + if (write_buffer_length_ > 0) { + HandleWritable(); + } +#endif // defined(FEATURE_ENABLE_SSL) + } else { + LOG(DFATAL) << "unexpected XmppSocketAdapter::OnConnectEvent state: " + << state_; + } +} + +void XmppSocketAdapter::OnReadEvent(talk_base::AsyncSocket *socket) { + HandleReadable(); +} + +void XmppSocketAdapter::OnWriteEvent(talk_base::AsyncSocket *socket) { + HandleWritable(); +} + +void XmppSocketAdapter::OnCloseEvent(talk_base::AsyncSocket *socket, + int error) { + LOG(INFO) << "XmppSocketAdapter::OnCloseEvent(" << error << ")"; + SetWSAError(error); + if (error == SOCKET_EACCES) { + SignalAuthenticationError(); // Proxy needs authentication. + } + NotifyClose(); +} + +#if defined(FEATURE_ENABLE_SSL) +bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) { + if (state_ != STATE_OPEN) { + SetError(ERROR_WRONGSTATE); + return false; + } + + state_ = STATE_TLS_CONNECTING; + + DCHECK_EQ(write_buffer_length_, 0U); + + talk_base::SSLAdapter* ssl_adapter = + static_cast<talk_base::SSLAdapter*>(socket_); + + if (allow_unverified_certs_) { + ssl_adapter->set_ignore_bad_cert(true); + } + + if (ssl_adapter->StartSSL(verify_host_name.c_str(), false) != 0) { + state_ = STATE_OPEN; + SetError(ERROR_SSL); + return false; + } + + return true; +} +#endif // defined(FEATURE_ENABLE_SSL) + +void XmppSocketAdapter::QueueWriteData(const char* data, size_t len) { + // Expand buffer if needed. + if (write_buffer_length_ + len > write_buffer_capacity_) { + size_t new_capacity = 1024; + while (new_capacity < write_buffer_length_ + len) { + new_capacity = new_capacity * 2; + } + char* new_buffer = new char[new_capacity]; + DCHECK_LE(write_buffer_length_, 64000U); + memcpy(new_buffer, write_buffer_, write_buffer_length_); + delete[] write_buffer_; + write_buffer_ = new_buffer; + write_buffer_capacity_ = new_capacity; + } + + // Copy data into the end of buffer. + memcpy(write_buffer_ + write_buffer_length_, data, len); + write_buffer_length_ += len; +} + +void XmppSocketAdapter::FlushWriteQueue(Error* error, int* wsa_error) { + DCHECK(error); + DCHECK(wsa_error); + + size_t flushed = 0; + while (flushed < write_buffer_length_) { + int sent = socket_->Send(write_buffer_ + flushed, + static_cast<int>(write_buffer_length_ - flushed)); + if (sent < 0) { + if (!socket_->IsBlocking()) { + *error = ERROR_WINSOCK; + *wsa_error = socket_->GetError(); + } + break; + } + flushed += static_cast<size_t>(sent); + } + + // Remove flushed memory. + write_buffer_length_ -= flushed; + memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_); + + // When everything is flushed, deallocate the buffer if it's gotten big. + if (write_buffer_length_ == 0) { + if (write_buffer_capacity_ > 8192) { + delete[] write_buffer_; + write_buffer_ = NULL; + write_buffer_capacity_ = 0; + } + } +} + +void XmppSocketAdapter::SetError(Error error) { + if (error_ == ERROR_NONE) { + error_ = error; + } +} + +void XmppSocketAdapter::SetWSAError(int error) { + if (error_ == ERROR_NONE && error != 0) { + error_ = ERROR_WINSOCK; + wsa_error_ = error; + } +} + +bool XmppSocketAdapter::HandleReadable() { + if (!IsOpen()) + return false; + + SignalRead(); + return true; +} + +bool XmppSocketAdapter::HandleWritable() { + if (!IsOpen()) + return false; + + Error error = ERROR_NONE; + int wsa_error = 0; + FlushWriteQueue(&error, &wsa_error); + if (error != ERROR_NONE) { + Close(); + return false; + } + return true; +} + +} // namespace remoting diff --git a/remoting/jingle_glue/xmpp_socket_adapter.h b/remoting/jingle_glue/xmpp_socket_adapter.h new file mode 100644 index 0000000..d1a6f65 --- /dev/null +++ b/remoting/jingle_glue/xmpp_socket_adapter.h @@ -0,0 +1,87 @@ +// 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 REMOTING_JINGLE_GLUE_XMPP_SOCKET_ADAPTER_H_ +#define REMOTING_JINGLE_GLUE_XMPP_SOCKET_ADAPTER_H_ + +#include <string> + +#include "talk/base/asyncsocket.h" +#include "talk/xmpp/asyncsocket.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/xmpp/xmppengine.h" + +#ifndef _WIN32 +// Additional errors used by us from Win32 headers. +#define SEC_E_CERT_EXPIRED static_cast<int>(0x80090328L) +#define WSA_NOT_ENOUGH_MEMORY ENOMEM +#endif + +namespace remoting { + +class XmppSocketAdapter : public buzz::AsyncSocket, + public sigslot::has_slots<> { + public: + XmppSocketAdapter(const buzz::XmppClientSettings& xcs, + bool allow_unverified_certs); + virtual ~XmppSocketAdapter(); + + virtual State state() { return state_; } + virtual Error error() { return error_; } + virtual int GetError() { return wsa_error_; } + + void set_firewall(bool firewall) { firewall_ = firewall; } + + virtual bool Connect(const talk_base::SocketAddress& addr); + virtual bool Read(char* data, size_t len, size_t* len_read); + virtual bool Write(const char* data, size_t len); + virtual bool Close(); + +#if defined(FEATURE_ENABLE_SSL) + bool StartTls(const std::string& domainname); + bool IsOpen() const { return state_ == STATE_OPEN + || state_ == STATE_TLS_OPEN; } +#else + bool IsOpen() const { return state_ == STATE_OPEN; } +#endif + + sigslot::signal0<> SignalAuthenticationError; + + private: + // Return false if the socket is closed. + bool HandleReadable(); + bool HandleWritable(); + + State state_; + Error error_; + int wsa_error_; + + talk_base::AsyncSocket* socket_; + cricket::ProtocolType protocol_; + talk_base::ProxyInfo proxy_; + bool firewall_; + char* write_buffer_; + size_t write_buffer_length_; + size_t write_buffer_capacity_; + bool allow_unverified_certs_; + + bool FreeState(); + void NotifyClose(); + + void OnReadEvent(talk_base::AsyncSocket* socket); + void OnWriteEvent(talk_base::AsyncSocket* socket); + void OnConnectEvent(talk_base::AsyncSocket* socket); + void OnCloseEvent(talk_base::AsyncSocket* socket, int error); + + void QueueWriteData(const char* data, size_t len); + void FlushWriteQueue(Error* error, int* wsa_error); + + void SetError(Error error); + void SetWSAError(int error); + DISALLOW_COPY_AND_ASSIGN(XmppSocketAdapter); +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_XMPP_SOCKET_ADAPTER_H_ |