diff options
-rw-r--r-- | remoting/DEPS | 5 | ||||
-rw-r--r-- | remoting/jingle_glue/gaia_token_pre_xmpp_auth.cc | 76 | ||||
-rw-r--r-- | remoting/jingle_glue/gaia_token_pre_xmpp_auth.h | 64 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_client.cc | 11 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_adapter.cc | 26 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_adapter.h | 35 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_socket_adapter.cc | 359 | ||||
-rw-r--r-- | remoting/jingle_glue/ssl_socket_adapter.h | 141 | ||||
-rw-r--r-- | remoting/jingle_glue/xmpp_socket_adapter.cc | 430 | ||||
-rw-r--r-- | remoting/jingle_glue/xmpp_socket_adapter.h | 90 | ||||
-rw-r--r-- | remoting/remoting.gyp | 31 |
11 files changed, 1245 insertions, 23 deletions
diff --git a/remoting/DEPS b/remoting/DEPS index d446efa..bb331a3 100644 --- a/remoting/DEPS +++ b/remoting/DEPS @@ -1,8 +1,11 @@ include_rules = [ - "+chrome/common", "+gfx", "+google/protobuf", "+media/base", + + # TODO(ajwong): Remove when xmpp code is out of jingle_glue. + "+net", + "+talk", "+third_party/npapi", "+third_party/zlib", diff --git a/remoting/jingle_glue/gaia_token_pre_xmpp_auth.cc b/remoting/jingle_glue/gaia_token_pre_xmpp_auth.cc new file mode 100644 index 0000000..0b31850 --- /dev/null +++ b/remoting/jingle_glue/gaia_token_pre_xmpp_auth.cc @@ -0,0 +1,76 @@ +// 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/gaia_token_pre_xmpp_auth.h" + +#include <algorithm> + +#include "talk/base/socketaddress.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/saslcookiemechanism.h" + +namespace remoting { + +namespace { +const char kGaiaAuthMechanism[] = "X-GOOGLE-TOKEN"; +} // namespace + +GaiaTokenPreXmppAuth::GaiaTokenPreXmppAuth( + const std::string& username, + const std::string& token, + const std::string& token_service) + : username_(username), + token_(token), + token_service_(token_service) { } + +GaiaTokenPreXmppAuth::~GaiaTokenPreXmppAuth() { } + +void GaiaTokenPreXmppAuth::StartPreXmppAuth( + const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string& auth_cookie) { + SignalAuthDone(); +} + +bool GaiaTokenPreXmppAuth::IsAuthDone() const { + return true; +} + +bool GaiaTokenPreXmppAuth::IsAuthorized() const { + return true; +} + +bool GaiaTokenPreXmppAuth::HadError() const { + return false; +} + +int GaiaTokenPreXmppAuth::GetError() const { + return 0; +} + +buzz::CaptchaChallenge GaiaTokenPreXmppAuth::GetCaptchaChallenge() const { + return buzz::CaptchaChallenge(); +} + +std::string GaiaTokenPreXmppAuth::GetAuthCookie() const { + return std::string(); +} + +std::string GaiaTokenPreXmppAuth::ChooseBestSaslMechanism( + const std::vector<std::string> & mechanisms, bool encrypted) { + return (std::find(mechanisms.begin(), + mechanisms.end(), kGaiaAuthMechanism) != + mechanisms.end()) ? kGaiaAuthMechanism : ""; +} + +buzz::SaslMechanism* GaiaTokenPreXmppAuth::CreateSaslMechanism( + const std::string& mechanism) { + if (mechanism != kGaiaAuthMechanism) + return NULL; + return new buzz::SaslCookieMechanism( + kGaiaAuthMechanism, username_, token_, token_service_); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/gaia_token_pre_xmpp_auth.h b/remoting/jingle_glue/gaia_token_pre_xmpp_auth.h new file mode 100644 index 0000000..8979c4a --- /dev/null +++ b/remoting/jingle_glue/gaia_token_pre_xmpp_auth.h @@ -0,0 +1,64 @@ +// 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. + +// TODO(ajwong): Unfork with +// chrome/common/net/notifier/communicator/gaia_token_pre_xmpp_auth.h + +#ifndef REMOTING_JINGLE_GLUE_GAIA_TOKEN_PRE_XMPP_AUTH_H_ +#define REMOTING_JINGLE_GLUE_GAIA_TOKEN_PRE_XMPP_AUTH_H_ + +#include <string> +#include <vector> + +#include "talk/xmpp/prexmppauth.h" + +namespace remoting { + +// This class implements buzz::PreXmppAuth interface for token-based +// authentication in GTalk. It looks for the X-GOOGLE-TOKEN auth mechanism +// and uses that instead of the default auth mechanism (PLAIN). +class GaiaTokenPreXmppAuth : public buzz::PreXmppAuth { + public: + GaiaTokenPreXmppAuth(const std::string& username, const std::string& token, + const std::string& token_service); + + virtual ~GaiaTokenPreXmppAuth(); + + // buzz::PreXmppAuth (-buzz::SaslHandler) implementation. We stub + // all the methods out as we don't actually do any authentication at + // this point. + virtual void StartPreXmppAuth(const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string& auth_cookie); + + virtual bool IsAuthDone() const; + + virtual bool IsAuthorized() const; + + virtual bool HadError() const; + + virtual int GetError() const; + + virtual buzz::CaptchaChallenge GetCaptchaChallenge() const; + + virtual std::string GetAuthCookie() const; + + // buzz::SaslHandler implementation. + + virtual std::string ChooseBestSaslMechanism( + const std::vector<std::string>& mechanisms, bool encrypted); + + virtual buzz::SaslMechanism* CreateSaslMechanism( + const std::string& mechanism); + + private: + std::string username_; + std::string token_; + std::string token_service_; +}; + +} // namespace remoting + +#endif // REMOTING_JINGLE_GLUE_GAIA_TOKEN_PRE_XMPP_AUTH_H_ diff --git a/remoting/jingle_glue/jingle_client.cc b/remoting/jingle_glue/jingle_client.cc index 415932c..5287d77 100644 --- a/remoting/jingle_glue/jingle_client.cc +++ b/remoting/jingle_glue/jingle_client.cc @@ -12,10 +12,10 @@ #include "base/logging.h" #include "base/waitable_event.h" #include "base/message_loop.h" -#include "chrome/common/net/notifier/communicator/gaia_token_pre_xmpp_auth.h" -#include "chrome/common/net/notifier/communicator/xmpp_socket_adapter.h" +#include "remoting/jingle_glue/gaia_token_pre_xmpp_auth.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" @@ -120,8 +120,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(); @@ -221,8 +220,8 @@ void JingleClient::UpdateState(State new_state) { buzz::PreXmppAuth* JingleClient::CreatePreXmppAuth( const buzz::XmppClientSettings& settings) { buzz::Jid jid(settings.user(), settings.host(), buzz::STR_EMPTY); - return new notifier::GaiaTokenPreXmppAuth(jid.Str(), settings.auth_cookie(), - settings.token_service()); + return new GaiaTokenPreXmppAuth(jid.Str(), settings.auth_cookie(), + settings.token_service()); } } // namespace remoting diff --git a/remoting/jingle_glue/ssl_adapter.cc b/remoting/jingle_glue/ssl_adapter.cc new file mode 100644 index 0000000..e22722f --- /dev/null +++ b/remoting/jingle_glue/ssl_adapter.cc @@ -0,0 +1,26 @@ +// 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..92a826a --- /dev/null +++ b/remoting/jingle_glue/ssl_adapter.h @@ -0,0 +1,35 @@ +// 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. + +// TODO(ajwong): Unfork with +// chrome/common/net/notifier/base/ssl_adapter.h + +#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..03abdc4 --- /dev/null +++ b/remoting/jingle_glue/ssl_socket_adapter.cc @@ -0,0 +1,359 @@ +// 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); +} + +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) { + socket_->SignalReadEvent.connect(this, &TransportSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &TransportSocket::OnWriteEvent); +} + +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; +} + +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::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)); + } +} + +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)); + } +} + +} // 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..17fbf24 --- /dev/null +++ b/remoting/jingle_glue/ssl_socket_adapter.h @@ -0,0 +1,141 @@ +// 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. + +// TODO(ajwong): Unfork with +// chrome/common/net/notifier/communicator/ssl_socket_adapter.h + +#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); + + 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_; } + + // 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_; + + 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::SSLAdapter { + public: + explicit SSLSocketAdapter(talk_base::AsyncSocket* socket); + + // 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..5755d1c --- /dev/null +++ b/remoting/jingle_glue/xmpp_socket_adapter.cc @@ -0,0 +1,430 @@ +// 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 { + +static const char kChromotingUserAgent[] = "chromoting"; + +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, + kChromotingUserAgent, 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 = 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..fa81045 --- /dev/null +++ b/remoting/jingle_glue/xmpp_socket_adapter.h @@ -0,0 +1,90 @@ +// 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. + +// TODO(ajwong): Unfork with +// chrome/common/net/notifier/communicator/xmpp_socket_adapter.h + +#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_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 8fdf56c..f8a2f89 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -252,9 +252,7 @@ 'target_name': 'chromoting_jingle_glue', 'type': '<(library)', 'dependencies': [ - # TODO(sergeyu): move all code that is shared between notifier and - # jingle_glue to a separate library and use it here. - '../chrome/chrome.gyp:notifier', + '../net/net.gyp:net', '../third_party/libjingle/libjingle.gyp:libjingle', '../third_party/libjingle/libjingle.gyp:libjingle_p2p', ], @@ -263,18 +261,26 @@ '../third_party/libjingle/libjingle.gyp:libjingle_p2p', ], 'sources': [ - 'jingle_glue/iq_request.h', + 'jingle_glue/gaia_token_pre_xmpp_auth.cc', + 'jingle_glue/gaia_token_pre_xmpp_auth.h', 'jingle_glue/iq_request.cc', - 'jingle_glue/jingle_channel.h', + 'jingle_glue/iq_request.h', 'jingle_glue/jingle_channel.cc', - 'jingle_glue/jingle_client.h', + 'jingle_glue/jingle_channel.h', 'jingle_glue/jingle_client.cc', - 'jingle_glue/jingle_info_task.h', + 'jingle_glue/jingle_client.h', 'jingle_glue/jingle_info_task.cc', - 'jingle_glue/jingle_thread.h', + 'jingle_glue/jingle_info_task.h', 'jingle_glue/jingle_thread.cc', - 'jingle_glue/relay_port_allocator.h', + 'jingle_glue/jingle_thread.h', 'jingle_glue/relay_port_allocator.cc', + 'jingle_glue/relay_port_allocator.h', + 'jingle_glue/ssl_adapter.cc', + 'jingle_glue/ssl_adapter.h', + 'jingle_glue/ssl_socket_adapter.cc', + 'jingle_glue/ssl_socket_adapter.h', + 'jingle_glue/xmpp_socket_adapter.cc', + 'jingle_glue/xmpp_socket_adapter.h', ], }, # end of target 'chromoting_jingle_glue' @@ -305,13 +311,6 @@ '../gfx/gfx.gyp:*', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', - # TODO(hclam): Remove all these extra dependencies notifier is moved - # outside of chrome.gyp. - '../chrome/chrome.gyp:browser', - '../chrome/chrome.gyp:chrome_version_info', - '../chrome/chrome.gyp:debugger', - '../chrome/chrome.gyp:plugin', - '../chrome/chrome.gyp:renderer', '../third_party/WebKit/WebKit/chromium/WebKit.gyp:webkit', '../webkit/support/webkit_support.gyp:appcache', '../webkit/support/webkit_support.gyp:database', |