summaryrefslogtreecommitdiffstats
path: root/remoting/protocol/jingle_session.cc
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-15 01:46:47 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-15 01:46:47 +0000
commit27f31bfabd6a4562b5b9d1f108c2fc992b597d92 (patch)
treeefa73b60228cbe4ea7dfa2689961571023036418 /remoting/protocol/jingle_session.cc
parentba9c9da4f6a32bc055d3003cf99d4af430aa83c7 (diff)
downloadchromium_src-27f31bfabd6a4562b5b9d1f108c2fc992b597d92.zip
chromium_src-27f31bfabd6a4562b5b9d1f108c2fc992b597d92.tar.gz
chromium_src-27f31bfabd6a4562b5b9d1f108c2fc992b597d92.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 Review URL: http://codereview.chromium.org/7326024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92641 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol/jingle_session.cc')
-rw-r--r--remoting/protocol/jingle_session.cc398
1 files changed, 151 insertions, 247 deletions
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());