diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-15 18:10:55 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-15 18:10:55 +0000 |
commit | f09abf28fd801c956267db53c5f3b069501bd428 (patch) | |
tree | 2317fbfa46169fa82330067b39175ff2e9aa412f /remoting/protocol | |
parent | b7ca15e0d73295f77d0877b766d3283b51eced20 (diff) | |
download | chromium_src-f09abf28fd801c956267db53c5f3b069501bd428.zip chromium_src-f09abf28fd801c956267db53c5f3b069501bd428.tar.gz chromium_src-f09abf28fd801c956267db53c5f3b069501bd428.tar.bz2 |
Move channel initialization logic to JingleChannel class.
Added new CreateChannel() method in Session interface. The new method can be used to create arbitrary channels.
BUG=None
TEST=Unittests
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=92641
Review URL: http://codereview.chromium.org/7326024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol')
-rw-r--r-- | remoting/protocol/fake_session.cc | 12 | ||||
-rw-r--r-- | remoting/protocol/fake_session.h | 5 | ||||
-rw-r--r-- | remoting/protocol/jingle_channel_connector.h | 44 | ||||
-rw-r--r-- | remoting/protocol/jingle_datagram_connector.cc | 49 | ||||
-rw-r--r-- | remoting/protocol/jingle_datagram_connector.h | 52 | ||||
-rw-r--r-- | remoting/protocol/jingle_session.cc | 398 | ||||
-rw-r--r-- | remoting/protocol/jingle_session.h | 144 | ||||
-rw-r--r-- | remoting/protocol/jingle_stream_connector.cc | 188 | ||||
-rw-r--r-- | remoting/protocol/jingle_stream_connector.h | 85 | ||||
-rw-r--r-- | remoting/protocol/protocol_mock_objects.h | 4 | ||||
-rw-r--r-- | remoting/protocol/session.h | 30 |
11 files changed, 667 insertions, 344 deletions
diff --git a/remoting/protocol/fake_session.cc b/remoting/protocol/fake_session.cc index c11fa34..2403a20 100644 --- a/remoting/protocol/fake_session.cc +++ b/remoting/protocol/fake_session.cc @@ -140,6 +140,18 @@ void FakeSession::SetStateChangeCallback( callback_.reset(callback); } +void FakeSession::CreateStreamChannel( + const std::string& name, const StreamChannelCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(name, NULL); +} + +void FakeSession::CreateDatagramChannel( + const std::string& name, const DatagramChannelCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(name, NULL); +} + FakeSocket* FakeSession::control_channel() { return &control_channel_; } diff --git a/remoting/protocol/fake_session.h b/remoting/protocol/fake_session.h index 1427bf3..d892ede 100644 --- a/remoting/protocol/fake_session.h +++ b/remoting/protocol/fake_session.h @@ -105,6 +105,11 @@ class FakeSession : public Session { virtual void SetStateChangeCallback(StateChangeCallback* callback); + virtual void CreateStreamChannel( + const std::string& name, const StreamChannelCallback& callback); + virtual void CreateDatagramChannel( + const std::string& name, const DatagramChannelCallback& callback); + virtual FakeSocket* control_channel(); virtual FakeSocket* event_channel(); virtual FakeSocket* video_channel(); diff --git a/remoting/protocol/jingle_channel_connector.h b/remoting/protocol/jingle_channel_connector.h new file mode 100644 index 0000000..6037ee4 --- /dev/null +++ b/remoting/protocol/jingle_channel_connector.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 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_PROTOCOL_JINGLE_CHANNEL_CONNECTOR_H_ +#define REMOTING_PROTOCOL_JINGLE_CHANNEL_CONNECTOR_H_ + +#include "base/basictypes.h" +#include "base/threading/non_thread_safe.h" + +namespace cricket { +class TransportChannel; +} // namespace cricket + +namespace crypto { +class RSAPrivateKey; +} // namespace crypto + +namespace net { +class X509Certificate; +} // namespace net + +namespace remoting { +namespace protocol { + +class JingleChannelConnector : public base::NonThreadSafe { + public: + JingleChannelConnector() { } + virtual ~JingleChannelConnector() { } + + virtual void Connect(bool initiator, + net::X509Certificate* local_cert, + net::X509Certificate* remote_cert, + crypto::RSAPrivateKey* local_private_key, + cricket::TransportChannel* raw_channel) = 0; + + protected: + DISALLOW_COPY_AND_ASSIGN(JingleChannelConnector); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_JINGLE_CHANNEL_CONNECTOR_H_ diff --git a/remoting/protocol/jingle_datagram_connector.cc b/remoting/protocol/jingle_datagram_connector.cc new file mode 100644 index 0000000..ff717a0 --- /dev/null +++ b/remoting/protocol/jingle_datagram_connector.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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/protocol/jingle_datagram_connector.h" + +#include "jingle/glue/channel_socket_adapter.h" +#include "remoting/protocol/jingle_session.h" +#include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" + +namespace remoting { +namespace protocol { + +JingleDatagramConnector::JingleDatagramConnector( + JingleSession* session, + const std::string& name, + const Session::DatagramChannelCallback& callback) + : session_(session), + name_(name), + callback_(callback) { +} + +JingleDatagramConnector::~JingleDatagramConnector() { +} + +void JingleDatagramConnector::Connect( + bool initiator, + net::X509Certificate* local_cert, + net::X509Certificate* remote_cert, + crypto::RSAPrivateKey* local_private_key, + cricket::TransportChannel* raw_channel) { + DCHECK(CalledOnValidThread()); + + if (!initiator) { + // Don't make outgoing connections from the host to client. + raw_channel->GetP2PChannel()->set_incoming_only(true); + } + + net::Socket* socket = + new jingle_glue::TransportChannelSocketAdapter(raw_channel); + + // TODO(sergeyu): Implement encryption for datagram channels. + + callback_.Run(name_, socket); + session_->OnChannelConnectorFinished(name_, this); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/jingle_datagram_connector.h b/remoting/protocol/jingle_datagram_connector.h new file mode 100644 index 0000000..f6b79d7 --- /dev/null +++ b/remoting/protocol/jingle_datagram_connector.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 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_PROTOCOL_JINGLE_DATAGRAM_CONNECTOR_H_ +#define REMOTING_PROTOCOL_JINGLE_DATAGRAM_CONNECTOR_H_ + +#include "net/base/completion_callback.h" +#include "remoting/protocol/jingle_channel_connector.h" +#include "remoting/protocol/session.h" + +namespace cricket { +class TransportChannel; +} // namespace cricket + +namespace jingle_glue { +class TransportChannelSocketAdapter; +} // namespace jingle_glue + +namespace remoting { +namespace protocol { + +class JingleSession; + +class JingleDatagramConnector : public JingleChannelConnector { + public: + JingleDatagramConnector(JingleSession* session, + const std::string& name, + const Session::DatagramChannelCallback& callback); + virtual ~JingleDatagramConnector(); + + // Starts connection process for the channel. |local_private_key| is + // owned by the caller, and must exist until this object is + // destroyed. + virtual void Connect(bool initiator, + net::X509Certificate* local_cert, + net::X509Certificate* remote_cert, + crypto::RSAPrivateKey* local_private_key, + cricket::TransportChannel* raw_channel) OVERRIDE; + + private: + JingleSession* session_; + std::string name_; + Session::DatagramChannelCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(JingleDatagramConnector); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_JINGLE_DATAGRAM_CONNECTOR_H_ diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc index ad417ef..f7d61f1 100644 --- a/remoting/protocol/jingle_session.cc +++ b/remoting/protocol/jingle_session.cc @@ -4,26 +4,21 @@ #include "remoting/protocol/jingle_session.h" +#include "base/bind.h" #include "base/message_loop.h" #include "base/rand_util.h" +#include "base/stl_util-inl.h" #include "crypto/hmac.h" #include "crypto/rsa_private_key.h" -#include "jingle/glue/channel_socket_adapter.h" -#include "jingle/glue/pseudotcp_adapter.h" -#include "net/base/cert_status_flags.h" -#include "net/base/cert_verifier.h" -#include "net/base/host_port_pair.h" #include "net/base/net_errors.h" -#include "net/base/ssl_config_service.h" -#include "net/base/x509_certificate.h" -#include "net/socket/client_socket_factory.h" -#include "net/socket/ssl_client_socket.h" -#include "net/socket/ssl_server_socket.h" +#include "net/socket/stream_socket.h" #include "remoting/base/constants.h" +#include "remoting/protocol/jingle_datagram_connector.h" #include "remoting/protocol/jingle_session_manager.h" +#include "remoting/protocol/jingle_stream_connector.h" #include "third_party/libjingle/source/talk/base/thread.h" #include "third_party/libjingle/source/talk/p2p/base/session.h" -#include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" +#include "third_party/libjingle/source/talk/p2p/base/transport.h" using cricket::BaseSession; @@ -44,34 +39,6 @@ const char kVideoRtcpChannelName[] = "videortcp"; const int kMasterKeyLength = 16; const int kChannelKeyLength = 16; -// Value is choosen to balance the extra latency against the reduced -// load due to ACK traffic. -const int kTcpAckDelayMilliseconds = 10; - -// Helper method to create a SSL client socket. -net::SSLClientSocket* CreateSSLClientSocket( - net::StreamSocket* socket, scoped_refptr<net::X509Certificate> cert, - net::CertVerifier* cert_verifier) { - net::SSLConfig ssl_config; - ssl_config.cached_info_enabled = false; - ssl_config.false_start_enabled = false; - ssl_config.ssl3_enabled = true; - ssl_config.tls1_enabled = true; - - // Certificate provided by the host doesn't need authority. - net::SSLConfig::CertAndStatus cert_and_status; - cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; - cert_and_status.cert = cert; - ssl_config.allowed_bad_certs.push_back(cert_and_status); - - // SSLClientSocket takes ownership of the adapter. - net::HostPortPair host_and_pair(JingleSession::kChromotingContentName, 0); - net::SSLClientSocket* ssl_socket = - net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( - socket, host_and_pair, ssl_config, NULL, cert_verifier); - return ssl_socket; -} - std::string GenerateRandomMasterKey() { std::string result; result.resize(kMasterKeyLength); @@ -142,10 +109,6 @@ JingleSession::JingleSession( closed_(false), closing_(false), cricket_session_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(connect_callback_( - this, &JingleSession::OnConnect)), - ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( - this, &JingleSession::OnSSLConnect)), ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { // TODO(hclam): Need a better way to clone a key. if (local_private_key) { @@ -169,7 +132,6 @@ void JingleSession::Init(cricket::Session* cricket_session) { cricket_session_ = cricket_session; jid_ = cricket_session_->remote_name(); - cert_verifier_.reset(new net::CertVerifier()); cricket_session_->SignalState.connect( this, &JingleSession::OnSessionState); cricket_session_->SignalError.connect( @@ -194,18 +156,13 @@ void JingleSession::CloseInternal(int result, bool failed) { else SetState(CLOSED); - // Now tear down the remoting channel resources. - control_channel_.reset(); - control_socket_.reset(); - control_ssl_socket_.reset(); - event_channel_.reset(); - event_socket_.reset(); - event_ssl_socket_.reset(); - video_channel_.reset(); - video_socket_.reset(); - video_ssl_socket_.reset(); - video_rtp_channel_.reset(); - video_rtcp_channel_.reset(); + control_channel_socket_.reset(); + event_channel_socket_.reset(); + video_channel_socket_.reset(); + video_rtp_channel_socket_.reset(); + video_rtcp_channel_socket_.reset(); + STLDeleteContainerPairSecondPointers(channel_connectors_.begin(), + channel_connectors_.end()); // Tear down the cricket session, including the cricket transport channels. if (cricket_session_) { @@ -215,7 +172,6 @@ void JingleSession::CloseInternal(int result, bool failed) { closed_ = true; } - cert_verifier_.reset(); } bool JingleSession::HasSession(cricket::Session* cricket_session) { @@ -242,30 +198,45 @@ void JingleSession::SetStateChangeCallback(StateChangeCallback* callback) { state_change_callback_.reset(callback); } +void JingleSession::CreateStreamChannel( + const std::string& name, const StreamChannelCallback& callback) { + DCHECK(CalledOnValidThread()); + + AddChannelConnector( + name, new JingleStreamConnector(this, name, callback)); +} + +void JingleSession::CreateDatagramChannel( + const std::string& name, const DatagramChannelCallback& callback) { + DCHECK(CalledOnValidThread()); + + AddChannelConnector( + name, new JingleDatagramConnector(this, name, callback)); +} + net::Socket* JingleSession::control_channel() { DCHECK(CalledOnValidThread()); - return control_ssl_socket_.get(); + return control_channel_socket_.get(); } net::Socket* JingleSession::event_channel() { DCHECK(CalledOnValidThread()); - return event_ssl_socket_.get(); + return event_channel_socket_.get(); } -// TODO(sergeyu): Remove this method after we switch to RTP. net::Socket* JingleSession::video_channel() { DCHECK(CalledOnValidThread()); - return video_ssl_socket_.get(); + return video_channel_socket_.get(); } net::Socket* JingleSession::video_rtp_channel() { DCHECK(CalledOnValidThread()); - return video_rtp_channel_.get(); + return video_rtp_channel_socket_.get(); } net::Socket* JingleSession::video_rtcp_channel() { DCHECK(CalledOnValidThread()); - return video_rtcp_channel_.get(); + return video_rtcp_channel_socket_.get(); } const std::string& JingleSession::jid() { @@ -390,22 +361,12 @@ void JingleSession::OnInitiate() { DCHECK(CalledOnValidThread()); jid_ = cricket_session_->remote_name(); - const cricket::SessionDescription* session_description; - // If we initiate the session, we get to specify the content name. When - // accepting one, the remote end specifies it. - if (cricket_session_->initiator()) { - session_description = cricket_session_->local_description(); - } else { - session_description = cricket_session_->remote_description(); - } - const cricket::ContentInfo* content = - session_description->FirstContentByType(kChromotingXmlNamespace); - CHECK(content); - const ContentDescription* content_description = - static_cast<const ContentDescription*>(content->description); - std::string content_name = content->name; - if (!cricket_session_->initiator()) { + const protocol::ContentDescription* content_description = + static_cast<const protocol::ContentDescription*>( + GetContentInfo()->description); + CHECK(content_description); + if (!DecryptMasterKey(local_private_key_.get(), content_description->master_key(), &master_key_)) { LOG(ERROR) << "Failed to decrypt master-key"; @@ -414,103 +375,23 @@ void JingleSession::OnInitiate() { } } - // Create video RTP channels. - raw_video_rtp_channel_ = - cricket_session_->CreateChannel(content_name, kVideoRtpChannelName); - video_rtp_channel_.reset( - new jingle_glue::TransportChannelSocketAdapter(raw_video_rtp_channel_)); - raw_video_rtcp_channel_ = - cricket_session_->CreateChannel(content_name, kVideoRtcpChannelName); - video_rtcp_channel_.reset( - new jingle_glue::TransportChannelSocketAdapter(raw_video_rtcp_channel_)); - - // Create control channel. - raw_control_channel_ = - cricket_session_->CreateChannel(content_name, kControlChannelName); - control_channel_.reset( - new jingle_glue::TransportChannelSocketAdapter(raw_control_channel_)); - - // Create event channel. - raw_event_channel_ = - cricket_session_->CreateChannel(content_name, kEventChannelName); - event_channel_.reset( - new jingle_glue::TransportChannelSocketAdapter(raw_event_channel_)); - - // Create video channel. - // TODO(wez): When we have RTP video support, we'll need to negotiate the - // type of video channel to allocate, for legacy compatibility. - raw_video_channel_ = - cricket_session_->CreateChannel(content_name, kVideoChannelName); - video_channel_.reset( - new jingle_glue::TransportChannelSocketAdapter(raw_video_channel_)); - - if (!cricket_session_->initiator()) { - if (!jingle_session_manager_->AcceptConnection(this, cricket_session_)) { - Close(); - // Release session so that - // JingleSessionManager::SessionDestroyed() doesn't try to call - // cricket::SessionManager::DestroySession() for it. - ReleaseSession(); - delete this; - return; - } - } - - // Set state to CONNECTING if the session is being accepted. - SetState(CONNECTING); -} - -bool JingleSession::EstablishPseudoTcp( - net::Socket* channel, - scoped_ptr<net::StreamSocket>* stream) { - jingle_glue::PseudoTcpAdapter* adapter = - new jingle_glue::PseudoTcpAdapter(channel); - adapter->SetAckDelay(kTcpAckDelayMilliseconds); - adapter->SetNoDelay(true); - - stream->reset(adapter); - int result = (*stream)->Connect(&connect_callback_); - return (result == net::OK) || (result == net::ERR_IO_PENDING); -} - -bool JingleSession::EstablishSSLConnection( - net::StreamSocket* socket, - scoped_ptr<net::StreamSocket>* ssl_socket) { - DCHECK(socket); - DCHECK(socket->IsConnected()); if (cricket_session_->initiator()) { - // Create client SSL socket. - net::SSLClientSocket* ssl_client_socket = CreateSSLClientSocket( - socket, remote_cert_, cert_verifier_.get()); - ssl_socket->reset(ssl_client_socket); - - int ret = ssl_client_socket->Connect(&ssl_connect_callback_); - if (ret == net::ERR_IO_PENDING) { - return true; - } else if (ret != net::OK) { - LOG(ERROR) << "Failed to establish SSL connection"; - cricket_session_->Terminate(); - return false; - } + // Set state to CONNECTING if this is an outgoing message. We need + // to post this task because channel creation works only after we + // return from this method. This is because + // JingleChannelConnector::Connect() needs to call + // set_incoming_only() on P2PTransportChannel, but + // P2PTransportChannel is created only after we return from this + // method. + // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. + MessageLoop::current()->PostTask( + FROM_HERE, task_factory_.NewRunnableMethod( + &JingleSession::SetState, CONNECTING)); } else { - // Create server SSL socket. - net::SSLConfig ssl_config; - net::SSLServerSocket* ssl_server_socket = net::CreateSSLServerSocket( - socket, local_cert_, local_private_key_.get(), ssl_config); - ssl_socket->reset(ssl_server_socket); - - int ret = ssl_server_socket->Handshake(&ssl_connect_callback_); - if (ret == net::ERR_IO_PENDING) { - return true; - } else if (ret != net::OK) { - LOG(ERROR) << "Failed to establish SSL connection"; - cricket_session_->Terminate(); - return false; - } + MessageLoop::current()->PostTask( + FROM_HERE, task_factory_.NewRunnableMethod( + &JingleSession::AcceptConnection)); } - // Reach here if net::OK is received. - ssl_connect_callback_.Run(net::OK); - return true; } bool JingleSession::InitializeConfigFromDescription( @@ -518,14 +399,13 @@ bool JingleSession::InitializeConfigFromDescription( // We should only be called after ParseContent has succeeded, in which // case there will always be a Chromoting session configuration. const cricket::ContentInfo* content = - description->FirstContentByType(kChromotingXmlNamespace); + description->FirstContentByType(kChromotingXmlNamespace); CHECK(content); const protocol::ContentDescription* content_description = - static_cast<const protocol::ContentDescription*>(content->description); + static_cast<const protocol::ContentDescription*>(content->description); CHECK(content_description); - remote_cert_ = content_description->certificate(); - if (!remote_cert_) { + remote_cert_ = content_description->certificate(); if (!remote_cert_) { LOG(ERROR) << "Connection response does not specify certificate"; return false; } @@ -545,29 +425,6 @@ bool JingleSession::InitializeConfigFromDescription( return true; } -void JingleSession::InitializeChannels() { - // Disable incoming connections on the host so that we don't traverse - // the firewall. - if (!cricket_session_->initiator()) { - raw_control_channel_->GetP2PChannel()->set_incoming_only(true); - raw_event_channel_->GetP2PChannel()->set_incoming_only(true); - raw_video_channel_->GetP2PChannel()->set_incoming_only(true); - raw_video_rtp_channel_->GetP2PChannel()->set_incoming_only(true); - raw_video_rtcp_channel_->GetP2PChannel()->set_incoming_only(true); - } - - // Create the Control, Event and Video connections on the channels. - if (!EstablishPseudoTcp(control_channel_.release(), - &control_socket_) || - !EstablishPseudoTcp(event_channel_.release(), - &event_socket_) || - !EstablishPseudoTcp(video_channel_.release(), - &video_socket_)) { - CloseInternal(net::ERR_CONNECTION_FAILED, true); - return; - } -} - void JingleSession::OnAccept() { DCHECK(CalledOnValidThread()); @@ -575,21 +432,13 @@ void JingleSession::OnAccept() { // host responded with, to refer to later. if (cricket_session_->initiator()) { if (!InitializeConfigFromDescription( - cricket_session_->remote_description())) { + cricket_session_->remote_description())) { CloseInternal(net::ERR_CONNECTION_FAILED, true); return; } } - // TODO(sergeyu): This is a hack: Currently set_incoming_only() - // needs to be called on each channel before the channel starts - // creating candidates but after session is accepted (after - // TransportChannelProxy::GetP2PChannel() starts returning actual - // P2P channel). By posting a task here we can call it at the right - // moment. This problem will go away when we switch to Pepper P2P - // API. - MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod( - &JingleSession::InitializeChannels)); + CreateChannels(); } void JingleSession::OnTerminate() { @@ -597,58 +446,113 @@ void JingleSession::OnTerminate() { CloseInternal(net::ERR_CONNECTION_ABORTED, false); } -void JingleSession::OnConnect(int result) { - DCHECK(CalledOnValidThread()); - - if (result != net::OK) { - LOG(ERROR) << "PseudoTCP connection failed: " << result; - CloseInternal(result, true); +void JingleSession::AcceptConnection() { + if (!jingle_session_manager_->AcceptConnection(this, cricket_session_)) { + Close(); + // Release session so that JingleSessionManager::SessionDestroyed() + // doesn't try to call cricket::SessionManager::DestroySession() for it. + ReleaseSession(); + delete this; return; } - if (control_socket_.get() && control_socket_->IsConnected()) { - if (!EstablishSSLConnection(control_socket_.release(), - &control_ssl_socket_)) { - LOG(ERROR) << "Establish control channel failed"; - CloseInternal(net::ERR_CONNECTION_FAILED, true); - return; - } - } - if (event_socket_.get() && event_socket_->IsConnected()) { - if (!EstablishSSLConnection(event_socket_.release(), - &event_ssl_socket_)) { - LOG(ERROR) << "Establish control event failed"; - CloseInternal(net::ERR_CONNECTION_FAILED, true); - return; - } - } - if (video_socket_.get() && video_socket_->IsConnected()) { - if (!EstablishSSLConnection(video_socket_.release(), - &video_ssl_socket_)) { - LOG(ERROR) << "Establish control video failed"; - CloseInternal(net::ERR_CONNECTION_FAILED, true); - return; - } - } + // Set state to CONNECTING if the session is being accepted. + SetState(CONNECTING); +} + +void JingleSession::AddChannelConnector( + const std::string& name, JingleChannelConnector* connector) { + DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); + + const std::string& content_name = GetContentInfo()->name; + cricket::TransportChannel* raw_channel = + cricket_session_->CreateChannel(content_name, name); + + channel_connectors_[name] = connector; + connector->Connect(cricket_session_->initiator(), local_cert_, + remote_cert_, local_private_key_.get(), raw_channel); + + // Workaround bug in libjingle - it doesn't connect channels if they + // are created after the session is accepted. See crbug.com/89384. + // TODO(sergeyu): Fix the bug and remove this line. + cricket_session_->GetTransport(content_name)->ConnectChannels(); } -void JingleSession::OnSSLConnect(int result) { +void JingleSession::OnChannelConnectorFinished( + const std::string& name, JingleChannelConnector* connector) { DCHECK(CalledOnValidThread()); + DCHECK_EQ(channel_connectors_[name], connector); + channel_connectors_[name] = NULL; + delete connector; +} + +void JingleSession::CreateChannels() { + StreamChannelCallback stream_callback( + base::Bind(&JingleSession::OnStreamChannelConnected, + base::Unretained(this))); + CreateStreamChannel(kControlChannelName, stream_callback); + CreateStreamChannel(kEventChannelName, stream_callback); + CreateStreamChannel(kVideoChannelName, stream_callback); + + DatagramChannelCallback datagram_callback( + base::Bind(&JingleSession::OnChannelConnected, + base::Unretained(this))); + CreateDatagramChannel(kVideoRtpChannelName, datagram_callback); + CreateDatagramChannel(kVideoRtcpChannelName, datagram_callback); +} + +void JingleSession::OnStreamChannelConnected(const std::string& name, + net::StreamSocket* socket) { + OnChannelConnected(name, socket); +} - DCHECK(!closed_); - if (result != net::OK) { - LOG(ERROR) << "Error during SSL connection: " << result; - CloseInternal(result, true); +void JingleSession::OnChannelConnected(const std::string& name, + net::Socket* socket) { + if (!socket) { + LOG(ERROR) << "Failed to connect channel " << name + << ". Terminating connection"; + CloseInternal(net::ERR_CONNECTION_CLOSED, true); return; } - if (event_ssl_socket_.get() && event_ssl_socket_->IsConnected() && - control_ssl_socket_.get() && control_ssl_socket_->IsConnected() && - video_ssl_socket_.get() && video_ssl_socket_->IsConnected()) { + if (name == kControlChannelName) { + control_channel_socket_.reset(socket); + } else if (name == kEventChannelName) { + event_channel_socket_.reset(socket); + } else if (name == kVideoChannelName) { + video_channel_socket_.reset(socket); + } else if (name == kVideoRtpChannelName) { + video_rtp_channel_socket_.reset(socket); + } else if (name == kVideoRtcpChannelName) { + video_rtcp_channel_socket_.reset(socket); + } else { + NOTREACHED(); + } + + if (control_channel_socket_.get() && event_channel_socket_.get() && + video_channel_socket_.get() && video_rtp_channel_socket_.get() && + video_rtcp_channel_socket_.get()) { + // TODO(sergeyu): State should be set to CONNECTED in OnAccept + // independent of the channels state. SetState(CONNECTED); } } +const cricket::ContentInfo* JingleSession::GetContentInfo() const { + const cricket::SessionDescription* session_description; + // If we initiate the session, we get to specify the content name. When + // accepting one, the remote end specifies it. + if (cricket_session_->initiator()) { + session_description = cricket_session_->local_description(); + } else { + session_description = cricket_session_->remote_description(); + } + const cricket::ContentInfo* content = + session_description->FirstContentByType(kChromotingXmlNamespace); + CHECK(content); + return content; +} + void JingleSession::SetState(State new_state) { DCHECK(CalledOnValidThread()); diff --git a/remoting/protocol/jingle_session.h b/remoting/protocol/jingle_session.h index c011e88..13a29bc 100644 --- a/remoting/protocol/jingle_session.h +++ b/remoting/protocol/jingle_session.h @@ -13,17 +13,7 @@ #include "third_party/libjingle/source/talk/base/sigslot.h" #include "third_party/libjingle/source/talk/p2p/base/session.h" -namespace jingle_glue { -class PseudoTcpAdapter; -class StreamSocketAdapter; -class TransportChannelSocketAdapter; -} // namespace jingle_glue - namespace net { -class CertVerifier; -class ClientSocketFactory; -class Socket; -class StreamSocket; class X509Certificate; } // namespace net @@ -31,6 +21,7 @@ namespace remoting { namespace protocol { +class JingleChannelConnector; class JingleSessionManager; // Implements protocol::Session that work over libjingle session (the @@ -41,32 +32,33 @@ class JingleSession : public protocol::Session, public: static const char kChromotingContentName[]; - // Chromotocol Session interface. - virtual void SetStateChangeCallback(StateChangeCallback* callback); - - virtual net::Socket* control_channel(); - virtual net::Socket* event_channel(); - virtual net::Socket* video_channel(); - - virtual net::Socket* video_rtp_channel(); - virtual net::Socket* video_rtcp_channel(); - - virtual const std::string& jid(); - - virtual const CandidateSessionConfig* candidate_config(); - - virtual const SessionConfig* config(); - virtual void set_config(const SessionConfig* config); - - virtual const std::string& initiator_token(); - virtual void set_initiator_token(const std::string& initiator_token); - virtual const std::string& receiver_token(); - virtual void set_receiver_token(const std::string& receiver_token); - - virtual void Close(); + // Session interface. + virtual void SetStateChangeCallback(StateChangeCallback* callback) OVERRIDE; + virtual void CreateStreamChannel( + const std::string& name, + const StreamChannelCallback& callback) OVERRIDE; + virtual void CreateDatagramChannel( + const std::string& name, + const DatagramChannelCallback& callback) OVERRIDE; + virtual net::Socket* control_channel() OVERRIDE; + virtual net::Socket* event_channel() OVERRIDE; + virtual net::Socket* video_channel() OVERRIDE; + virtual net::Socket* video_rtp_channel() OVERRIDE; + virtual net::Socket* video_rtcp_channel() OVERRIDE; + virtual const std::string& jid() OVERRIDE; + virtual const CandidateSessionConfig* candidate_config() OVERRIDE; + virtual const SessionConfig* config() OVERRIDE; + virtual void set_config(const SessionConfig* config) OVERRIDE; + virtual const std::string& initiator_token() OVERRIDE; + virtual void set_initiator_token(const std::string& initiator_token) OVERRIDE; + virtual const std::string& receiver_token() OVERRIDE; + virtual void set_receiver_token(const std::string& receiver_token) OVERRIDE; + virtual void Close() OVERRIDE; private: + friend class JingleDatagramConnector; friend class JingleSessionManager; + friend class JingleStreamConnector; // Create a JingleSession used in client mode. A server certificate is // required. @@ -104,20 +96,7 @@ class JingleSession : public protocol::Session, // Initialize the session configuration from a received connection response // stanza. bool InitializeConfigFromDescription( - const cricket::SessionDescription* description); - - // Configures channels and calls InitializeSSL(). - void InitializeChannels(); - - // Helper method to initialize PseudoTcp over the datagram-oriented |channel|. - // If things don't immediately fail then a new StreamSocket is placed in - // |pseudo_tcp|. - bool EstablishPseudoTcp(net::Socket* channel, - scoped_ptr<net::StreamSocket>* pseudo_tcp); - - // Helper method to initialize SSL over a StreamSocket. - bool EstablishSSLConnection(net::StreamSocket* socket, - scoped_ptr<net::StreamSocket>* ssl_socket); + const cricket::SessionDescription* description); // Used for Session.SignalState sigslot. void OnSessionState(cricket::BaseSession* session, @@ -130,10 +109,30 @@ class JingleSession : public protocol::Session, void OnAccept(); void OnTerminate(); - void OnConnect(int result); + // Notifies upper layer about incoming connection and + // accepts/rejects connection. + void AcceptConnection(); + + void AddChannelConnector(const std::string& name, + JingleChannelConnector* connector); - // Called by SSL socket to notify connect event. - void OnSSLConnect(int result); + // Called by JingleChannelConnector when it has finished connecting + // the channel and needs to be destroyed. + void OnChannelConnectorFinished(const std::string& name, + JingleChannelConnector* connector); + + // Creates channels after session has been accepted. + // TODO(sergeyu): Don't create channels in JingleSession. + void CreateChannels(); + + // Callbacks for the channels created in JingleSession. + // TODO(sergeyu): Remove this method once *_channel() methods are + // removed from Session interface. + void OnStreamChannelConnected( + const std::string& name, net::StreamSocket* socket); + void OnChannelConnected(const std::string& name, net::Socket* socket); + + const cricket::ContentInfo* GetContentInfo() const; void SetState(State new_state); @@ -178,43 +177,14 @@ class JingleSession : public protocol::Session, // These data members are only set on the receiving side. scoped_ptr<const CandidateSessionConfig> candidate_config_; - // |raw_foo_channel_| is a reference to the Cricket transport channel foo, - // which is owned by the Cricket session. - // |foo_channel_| owns the Socket adapter for that channel. - // |foo_socket_| takes ownership of the Socket from |foo_channel_| and wraps - // it with PseudoTcp. |foo_ssl_socket_| takes ownership from |foo_socket_| - // and runs the SSL protocol over it. Finally |foo_socket_wrapper_| takes - // ownership of |foo_ssl_socket_|, wrapping it with a SocketWrapper to work - // around issues with callers of the resulting Sockets continuing to access - // them after CloseInternal() has completed. - // TODO(wez): Fix the tear-down issue and remove SocketWrapper. - - cricket::TransportChannel* raw_control_channel_; - scoped_ptr<jingle_glue::TransportChannelSocketAdapter> control_channel_; - scoped_ptr<net::StreamSocket> control_socket_; - scoped_ptr<net::StreamSocket> control_ssl_socket_; - - cricket::TransportChannel* raw_event_channel_; - scoped_ptr<jingle_glue::TransportChannelSocketAdapter> event_channel_; - scoped_ptr<net::StreamSocket> event_socket_; - scoped_ptr<net::StreamSocket> event_ssl_socket_; - - cricket::TransportChannel* raw_video_channel_; - scoped_ptr<jingle_glue::TransportChannelSocketAdapter> video_channel_; - scoped_ptr<net::StreamSocket> video_socket_; - scoped_ptr<net::StreamSocket> video_ssl_socket_; - - // Used to verify the certificate received in SSLClientSocket. - scoped_ptr<net::CertVerifier> cert_verifier_; - - cricket::TransportChannel* raw_video_rtp_channel_; - scoped_ptr<jingle_glue::TransportChannelSocketAdapter> video_rtp_channel_; - cricket::TransportChannel* raw_video_rtcp_channel_; - scoped_ptr<jingle_glue::TransportChannelSocketAdapter> video_rtcp_channel_; - - // Callback called by the SSL layer. - net::CompletionCallbackImpl<JingleSession> connect_callback_; - net::CompletionCallbackImpl<JingleSession> ssl_connect_callback_; + // Channels that are currently being connected. + std::map<std::string, JingleChannelConnector*> channel_connectors_; + + scoped_ptr<net::Socket> control_channel_socket_; + scoped_ptr<net::Socket> event_channel_socket_; + scoped_ptr<net::Socket> video_channel_socket_; + scoped_ptr<net::Socket> video_rtp_channel_socket_; + scoped_ptr<net::Socket> video_rtcp_channel_socket_; ScopedRunnableMethodFactory<JingleSession> task_factory_; diff --git a/remoting/protocol/jingle_stream_connector.cc b/remoting/protocol/jingle_stream_connector.cc new file mode 100644 index 0000000..ea541b0 --- /dev/null +++ b/remoting/protocol/jingle_stream_connector.cc @@ -0,0 +1,188 @@ +// Copyright (c) 2011 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/protocol/jingle_stream_connector.h" + +#include "jingle/glue/channel_socket_adapter.h" +#include "jingle/glue/pseudotcp_adapter.h" +#include "net/base/cert_status_flags.h" +#include "net/base/cert_verifier.h" +#include "net/base/host_port_pair.h" +#include "net/base/ssl_config_service.h" +#include "net/base/x509_certificate.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_server_socket.h" +#include "net/socket/client_socket_factory.h" +#include "remoting/protocol/jingle_session.h" +#include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" + +namespace remoting { +namespace protocol { + +namespace { + +// Value is choosen to balance the extra latency against the reduced +// load due to ACK traffic. +const int kTcpAckDelayMilliseconds = 10; + +// Helper method to create a SSL client socket. +net::SSLClientSocket* CreateSSLClientSocket( + net::StreamSocket* socket, scoped_refptr<net::X509Certificate> cert, + net::CertVerifier* cert_verifier) { + net::SSLConfig ssl_config; + + // Certificate provided by the host doesn't need authority. + net::SSLConfig::CertAndStatus cert_and_status; + cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; + cert_and_status.cert = cert; + ssl_config.allowed_bad_certs.push_back(cert_and_status); + + // SSLClientSocket takes ownership of the adapter. + net::HostPortPair host_and_pair(JingleSession::kChromotingContentName, 0); + net::SSLClientSocket* ssl_socket = + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + socket, host_and_pair, ssl_config, NULL, cert_verifier); + return ssl_socket; +} + +} // namespace + +JingleStreamConnector::JingleStreamConnector( + JingleSession* session, + const std::string& name, + const Session::StreamChannelCallback& callback) + : session_(session), + name_(name), + callback_(callback), + initiator_(false), + local_private_key_(NULL), + raw_channel_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(tcp_connect_callback_( + this, &JingleStreamConnector::OnTCPConnect)), + ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( + this, &JingleStreamConnector::OnSSLConnect)) { +} + +JingleStreamConnector::~JingleStreamConnector() { +} + +void JingleStreamConnector::Connect(bool initiator, + net::X509Certificate* local_cert, + net::X509Certificate* remote_cert, + crypto::RSAPrivateKey* local_private_key, + cricket::TransportChannel* raw_channel) { + DCHECK(CalledOnValidThread()); + DCHECK(!raw_channel_); + + initiator_ = initiator; + local_cert_ = local_cert; + remote_cert_ = remote_cert; + local_private_key_ = local_private_key; + raw_channel_ = raw_channel; + + if (!initiator_) { + // Don't make outgoing connections from the host to client. + raw_channel_->GetP2PChannel()->set_incoming_only(true); + } + + net::Socket* socket = + new jingle_glue::TransportChannelSocketAdapter(raw_channel_); + + if (!EstablishTCPConnection(socket)) + NotifyError(); +} + +bool JingleStreamConnector::EstablishTCPConnection(net::Socket* socket) { + jingle_glue::PseudoTcpAdapter* adapter = + new jingle_glue::PseudoTcpAdapter(socket); + adapter->SetAckDelay(kTcpAckDelayMilliseconds); + adapter->SetNoDelay(true); + + socket_.reset(adapter); + int result = socket_->Connect(&tcp_connect_callback_); + if (result == net::ERR_IO_PENDING) { + return true; + } else if (result == net::OK) { + tcp_connect_callback_.Run(result); + return true; + } + + return false; +} + +bool JingleStreamConnector::EstablishSSLConnection() { + DCHECK(socket_->IsConnected()); + + int result; + if (initiator_) { + cert_verifier_.reset(new net::CertVerifier()); + + // Create client SSL socket. + net::SSLClientSocket* ssl_client_socket = CreateSSLClientSocket( + socket_.release(), remote_cert_, cert_verifier_.get()); + socket_.reset(ssl_client_socket); + + result = ssl_client_socket->Connect(&ssl_connect_callback_); + } else { + // Create server SSL socket. + net::SSLConfig ssl_config; + net::SSLServerSocket* ssl_server_socket = + net::CreateSSLServerSocket(socket_.release(), local_cert_, + local_private_key_, ssl_config); + socket_.reset(ssl_server_socket); + + result = ssl_server_socket->Handshake(&ssl_connect_callback_); + } + + if (result == net::ERR_IO_PENDING) { + return true; + } else if (result != net::OK) { + LOG(ERROR) << "Failed to establish SSL connection"; + return false; + } + + // Reach here if net::OK is received. + ssl_connect_callback_.Run(net::OK); + return true; +} + +void JingleStreamConnector::OnTCPConnect(int result) { + DCHECK(CalledOnValidThread()); + + if (result != net::OK) { + LOG(ERROR) << "PseudoTCP connection failed: " << result; + NotifyError(); + return; + } + + if (!EstablishSSLConnection()) + NotifyError(); +} + +void JingleStreamConnector::OnSSLConnect(int result) { + DCHECK(CalledOnValidThread()); + + if (result != net::OK) { + LOG(ERROR) << "Error during SSL connection: " << result; + NotifyError(); + return; + } + + DCHECK(socket_->IsConnected()); + NotifyDone(socket_.release()); +} + +void JingleStreamConnector::NotifyDone(net::StreamSocket* socket) { + callback_.Run(name_, socket); + session_->OnChannelConnectorFinished(name_, this); +} + +void JingleStreamConnector::NotifyError() { + socket_.reset(); + callback_.Run(name_, NULL); + session_->OnChannelConnectorFinished(name_, this); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/jingle_stream_connector.h b/remoting/protocol/jingle_stream_connector.h new file mode 100644 index 0000000..ab7968b --- /dev/null +++ b/remoting/protocol/jingle_stream_connector.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 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_PROTOCOL_JINGLE_STREAM_CONNECTOR_H_ +#define REMOTING_PROTOCOL_JINGLE_STREAM_CONNECTOR_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "remoting/protocol/jingle_channel_connector.h" +#include "remoting/protocol/session.h" + +namespace cricket { +class TransportChannel; +} // namespace cricket + +namespace jingle_glue { +class TransportChannelSocketAdapter; +} // namespace jingle_glue + +namespace net { +class CertVerifier; +class StreamSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +class JingleSession; + +class JingleStreamConnector : public JingleChannelConnector { + public: + JingleStreamConnector(JingleSession* session, + const std::string& name, + const Session::StreamChannelCallback& callback); + virtual ~JingleStreamConnector(); + + // Starts connection process for the channel. |local_private_key| is + // owned by the caller, and must exist until this object is + // destroyed. + virtual void Connect(bool initiator, + net::X509Certificate* local_cert, + net::X509Certificate* remote_cert, + crypto::RSAPrivateKey* local_private_key, + cricket::TransportChannel* raw_channel) OVERRIDE; + + private: + bool EstablishTCPConnection(net::Socket* socket); + void OnTCPConnect(int result); + + bool EstablishSSLConnection(); + void OnSSLConnect(int result); + + void NotifyDone(net::StreamSocket* socket); + void NotifyError(); + + JingleSession* session_; + + std::string name_; + + Session::StreamChannelCallback callback_; + + bool initiator_; + scoped_refptr<net::X509Certificate> local_cert_; + scoped_refptr<net::X509Certificate> remote_cert_; + crypto::RSAPrivateKey* local_private_key_; + + cricket::TransportChannel* raw_channel_; + scoped_ptr<net::StreamSocket> socket_; + + // Used to verify the certificate received in SSLClientSocket. + scoped_ptr<net::CertVerifier> cert_verifier_; + + // Callback called by the TCP and SSL layers. + net::CompletionCallbackImpl<JingleStreamConnector> tcp_connect_callback_; + net::CompletionCallbackImpl<JingleStreamConnector> ssl_connect_callback_; + + DISALLOW_COPY_AND_ASSIGN(JingleStreamConnector); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_JINGLE_STREAM_CONNECTOR_H_ diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h index 274b304..9b34b8e 100644 --- a/remoting/protocol/protocol_mock_objects.h +++ b/remoting/protocol/protocol_mock_objects.h @@ -112,6 +112,10 @@ class MockSession : public Session { virtual ~MockSession(); MOCK_METHOD1(SetStateChangeCallback, void(StateChangeCallback* callback)); + MOCK_METHOD2(CreateStreamChannel, void( + const std::string& name, const StreamChannelCallback& callback)); + MOCK_METHOD2(CreateDatagramChannel, void( + const std::string& name, const DatagramChannelCallback& callback)); MOCK_METHOD0(control_channel, net::Socket*()); MOCK_METHOD0(event_channel, net::Socket*()); MOCK_METHOD0(video_channel, net::Socket*()); diff --git a/remoting/protocol/session.h b/remoting/protocol/session.h index 754b223..059a21f 100644 --- a/remoting/protocol/session.h +++ b/remoting/protocol/session.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -16,6 +16,7 @@ class Task; namespace net { class Socket; +class StreamSocket; } // namespace net namespace remoting { @@ -24,7 +25,6 @@ namespace protocol { // Generic interface for Chromotocol connection used by both client and host. // Provides access to the connection channels, but doesn't depend on the // protocol used for each channel. -// TODO(sergeyu): Remove refcounting? class Session : public base::NonThreadSafe { public: enum State { @@ -36,6 +36,10 @@ class Session : public base::NonThreadSafe { }; typedef Callback1<State>::Type StateChangeCallback; + typedef base::Callback<void(const std::string&, net::StreamSocket*)> + StreamChannelCallback; + typedef base::Callback<void(const std::string&, net::Socket*)> + DatagramChannelCallback; Session() { } virtual ~Session() { } @@ -44,20 +48,26 @@ class Session : public base::NonThreadSafe { // Must be called on the jingle thread only. virtual void SetStateChangeCallback(StateChangeCallback* callback) = 0; - // Reliable PseudoTCP channels for this connection. + // Creates new channels for this connection. The specified callback + // is called when then new channel is created and connected. The + // callback is called with NULL if connection failed for any reason. + // Ownership of the channel socket is given to the caller when the + // callback is called. All channels must be destroyed before the + // session is destroyed. Can be called only when in CONNECTING or + // CONNECTED state. + virtual void CreateStreamChannel( + const std::string& name, const StreamChannelCallback& callback) = 0; + virtual void CreateDatagramChannel( + const std::string& name, const DatagramChannelCallback& callback) = 0; + + // TODO(sergeyu): Remove these methods, and use CreateChannel() + // instead. virtual net::Socket* control_channel() = 0; virtual net::Socket* event_channel() = 0; - - // TODO(sergeyu): Remove VideoChannel, and use RTP channels instead. virtual net::Socket* video_channel() = 0; - - // Unreliable channels for this connection. virtual net::Socket* video_rtp_channel() = 0; virtual net::Socket* video_rtcp_channel() = 0; - // TODO(sergeyu): Make it possible to create/destroy additional channels - // on-fly? - // JID of the other side. virtual const std::string& jid() = 0; |