summaryrefslogtreecommitdiffstats
path: root/remoting/jingle_glue
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-10 20:03:15 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-10 20:03:15 +0000
commitf73e174dbd1fcd3ef9ca23dbe0a4e789068ee020 (patch)
treea6f7446f28a742d9b190fd78ee8276245d3bca8d /remoting/jingle_glue
parent833d617cffb3de3e90638d963a73aa7158b6843a (diff)
downloadchromium_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.cc4
-rw-r--r--remoting/jingle_glue/ssl_adapter.cc27
-rw-r--r--remoting/jingle_glue/ssl_adapter.h33
-rw-r--r--remoting/jingle_glue/ssl_socket_adapter.cc386
-rw-r--r--remoting/jingle_glue/ssl_socket_adapter.h145
-rw-r--r--remoting/jingle_glue/xmpp_socket_adapter.cc428
-rw-r--r--remoting/jingle_glue/xmpp_socket_adapter.h87
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_