diff options
-rw-r--r-- | jingle/glue/pseudotcp_adapter.cc | 4 | ||||
-rw-r--r-- | remoting/base/constants.cc | 2 | ||||
-rw-r--r-- | remoting/base/constants.h | 2 | ||||
-rw-r--r-- | remoting/protocol/jingle_session.cc | 7 | ||||
-rw-r--r-- | remoting/protocol/pepper_channel.h | 50 | ||||
-rw-r--r-- | remoting/protocol/pepper_session.cc | 396 | ||||
-rw-r--r-- | remoting/protocol/pepper_session.h | 168 | ||||
-rw-r--r-- | remoting/protocol/pepper_session_manager.cc | 146 | ||||
-rw-r--r-- | remoting/protocol/pepper_session_manager.h | 105 | ||||
-rw-r--r-- | remoting/protocol/pepper_stream_channel.cc | 264 | ||||
-rw-r--r-- | remoting/protocol/pepper_stream_channel.h | 92 | ||||
-rw-r--r-- | remoting/protocol/pepper_transport_socket_adapter.cc | 14 | ||||
-rw-r--r-- | remoting/protocol/pepper_transport_socket_adapter.h | 2 | ||||
-rw-r--r-- | remoting/protocol/transport_config.cc | 18 | ||||
-rw-r--r-- | remoting/protocol/transport_config.h | 26 | ||||
-rw-r--r-- | remoting/remoting.gyp | 9 |
16 files changed, 1290 insertions, 15 deletions
diff --git a/jingle/glue/pseudotcp_adapter.cc b/jingle/glue/pseudotcp_adapter.cc index e2b3130..94c81aa 100644 --- a/jingle/glue/pseudotcp_adapter.cc +++ b/jingle/glue/pseudotcp_adapter.cc @@ -447,8 +447,8 @@ bool PseudoTcpAdapter::IsConnectedAndIdle() const { int PseudoTcpAdapter::GetPeerAddress(net::AddressList* address) const { DCHECK(CalledOnValidThread()); - // We actually don't know the peer address. Returning so the upper layers - // won't complain. + // We don't have a meaningful peer address, but we can't return an + // error, so we return a INADDR_ANY instead. net::IPAddressNumber ip_address(4); *address = net::AddressList::CreateFromIPAddress(ip_address, 0); return net::OK; diff --git a/remoting/base/constants.cc b/remoting/base/constants.cc index 90ea755..178ec91 100644 --- a/remoting/base/constants.cc +++ b/remoting/base/constants.cc @@ -12,6 +12,8 @@ const char kChromotingTokenDefaultServiceName[] = "chromiumsync"; const char kChromotingXmlNamespace[] = "google:remoting"; +const char kControlChannelName[] = "control"; +const char kEventChannelName[] = "event"; const char kVideoChannelName[] = "video"; const char kVideoRtpChannelName[] = "videortp"; const char kVideoRtcpChannelName[] = "videortpc"; diff --git a/remoting/base/constants.h b/remoting/base/constants.h index 6efa408..ed95ccc 100644 --- a/remoting/base/constants.h +++ b/remoting/base/constants.h @@ -18,6 +18,8 @@ extern const char kChromotingTokenDefaultServiceName[]; extern const char kChromotingXmlNamespace[]; // Channel names. +extern const char kControlChannelName[]; +extern const char kEventChannelName[]; extern const char kVideoChannelName[]; extern const char kVideoRtpChannelName[]; extern const char kVideoRtcpChannelName[]; diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc index b80239c..6d78f55 100644 --- a/remoting/protocol/jingle_session.cc +++ b/remoting/protocol/jingle_session.cc @@ -26,13 +26,6 @@ using cricket::BaseSession; namespace remoting { namespace protocol { -namespace { - -const char kControlChannelName[] = "control"; -const char kEventChannelName[] = "event"; - -} // namespace - // static JingleSession* JingleSession::CreateClientSession( JingleSessionManager* manager, const std::string& host_public_key) { diff --git a/remoting/protocol/pepper_channel.h b/remoting/protocol/pepper_channel.h new file mode 100644 index 0000000..6ae46bb --- /dev/null +++ b/remoting/protocol/pepper_channel.h @@ -0,0 +1,50 @@ +// 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_PEPPER_CHANNEL_H_ +#define REMOTING_PROTOCOL_PEPPER_CHANNEL_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/threading/non_thread_safe.h" + +namespace pp { +class Instance; +} // namespace pp + +namespace cricket { +class Candidate; +} // namespace cricket + +namespace remoting { +namespace protocol { + +struct TransportConfig; + +// Interface for stream and datagram channels used by PepperSession. +class PepperChannel : public base::NonThreadSafe { + public: + PepperChannel() { } + virtual ~PepperChannel() { } + + // Connect the channel using specified |config|. + virtual void Connect(pp::Instance* pp_instance, + const TransportConfig& config, + const std::string& remote_cert) = 0; + + // Adds |candidate| received from the peer. + virtual void AddRemoveCandidate(const cricket::Candidate& candidate) = 0; + + // Name of the channel. + virtual const std::string& name() = 0; + + protected: + DISALLOW_COPY_AND_ASSIGN(PepperChannel); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_PEPPER_CHANNEL_H_ diff --git a/remoting/protocol/pepper_session.cc b/remoting/protocol/pepper_session.cc new file mode 100644 index 0000000..03167a90 --- /dev/null +++ b/remoting/protocol/pepper_session.cc @@ -0,0 +1,396 @@ +// 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/pepper_session.h" + +#include "base/bind.h" +#include "base/rand_util.h" +#include "base/stl_util.h" +#include "base/string_number_conversions.h" +#include "remoting/base/constants.h" +#include "remoting/jingle_glue/iq_request.h" +#include "remoting/protocol/content_description.h" +#include "remoting/protocol/jingle_messages.h" +#include "remoting/protocol/pepper_session_manager.h" +#include "remoting/protocol/pepper_stream_channel.h" +#include "third_party/libjingle/source/talk/p2p/base/candidate.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" + +using buzz::XmlElement; + +namespace remoting { +namespace protocol { + +namespace { +// Delay after candidate creation before sending transport-info +// message. This is neccessary to be able to pack multiple candidates +// into one transport-info messages. The value needs to be greater +// than zero because ports are opened asynchronously in the browser +// process. +const int kTransportInfoSendDelayMs = 2; +} // namespace + +PepperSession::PepperSession(PepperSessionManager* session_manager) + : session_manager_(session_manager), + state_(INITIALIZING), + error_(ERROR_NO_ERROR) { +} + +PepperSession::~PepperSession() { + control_channel_socket_.reset(); + event_channel_socket_.reset(); + STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); + session_manager_->SessionDestroyed(this); +} + +PepperSession::Error PepperSession::error() { + DCHECK(CalledOnValidThread()); + return error_; +} + +void PepperSession::SetStateChangeCallback(StateChangeCallback* callback) { + DCHECK(CalledOnValidThread()); + state_change_callback_.reset(callback); +} + +void PepperSession::StartConnection( + const std::string& peer_jid, + const std::string& peer_public_key, + const std::string& client_token, + CandidateSessionConfig* config, + Session::StateChangeCallback* state_change_callback) { + DCHECK(CalledOnValidThread()); + + peer_jid_ = peer_jid; + peer_public_key_ = peer_public_key; + initiator_token_ = client_token; + candidate_config_.reset(config); + state_change_callback_.reset(state_change_callback); + + // Generate random session ID. There are usually not more than 1 + // concurrent session per host, so a random 64-bit integer provides + // enough entropy. In the worst case connection will fail when two + // clients generate the same session ID concurrently. + session_id_ = base::Int64ToString(base::RandGenerator(kint64max)); + + // Send session-initiate message. + JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE, + session_id_); + message.from = session_manager_->local_jid_; + message.description.reset( + new ContentDescription(candidate_config_->Clone(), initiator_token_, "")); + initiate_request_.reset(session_manager_->CreateIqRequest()); + initiate_request_->set_callback(base::Bind( + &PepperSession::OnSessionInitiateResponse, base::Unretained(this))); + initiate_request_->SendIq(message.ToXml()); + + SetState(CONNECTING); +} + +void PepperSession::OnSessionInitiateResponse( + const buzz::XmlElement* response) { + const std::string& type = response->Attr(buzz::QName("", "type")); + if (type != "result") { + LOG(ERROR) << "Received error in response to session-initiate message: \"" + << response->Str() + << "\" Terminating the session."; + + // TODO(sergeyu): There may be different reasons for error + // here. Parse the response stanza to find failure reason. + OnError(ERROR_PEER_IS_OFFLINE); + } +} + +void PepperSession::OnError(Error error) { + error_ = error; + CloseInternal(true); +} + +void PepperSession::CreateStreamChannel( + const std::string& name, + const StreamChannelCallback& callback) { + DCHECK(!channels_[name]); + + PepperStreamChannel* channel = new PepperStreamChannel(this, name, callback); + channels_[name] = channel; + channel->Connect(session_manager_->pp_instance_, + session_manager_->transport_config_, remote_cert_); +} + +void PepperSession::CreateDatagramChannel( + const std::string& name, + const DatagramChannelCallback& callback) { + // TODO(sergeyu): Implement datagram channel support. + NOTREACHED(); +} + +net::Socket* PepperSession::control_channel() { + DCHECK(CalledOnValidThread()); + return control_channel_socket_.get(); +} + +net::Socket* PepperSession::event_channel() { + DCHECK(CalledOnValidThread()); + return event_channel_socket_.get(); +} + +const std::string& PepperSession::jid() { + DCHECK(CalledOnValidThread()); + return peer_jid_; +} + +const CandidateSessionConfig* PepperSession::candidate_config() { + DCHECK(CalledOnValidThread()); + return candidate_config_.get(); +} + +const SessionConfig& PepperSession::config() { + DCHECK(CalledOnValidThread()); + return config_; +} + +void PepperSession::set_config(const SessionConfig& config) { + DCHECK(CalledOnValidThread()); + // set_config() should never be called on the client. + NOTREACHED(); +} + +const std::string& PepperSession::initiator_token() { + DCHECK(CalledOnValidThread()); + return initiator_token_; +} + +void PepperSession::set_initiator_token(const std::string& initiator_token) { + DCHECK(CalledOnValidThread()); + initiator_token_ = initiator_token; +} + +const std::string& PepperSession::receiver_token() { + DCHECK(CalledOnValidThread()); + return receiver_token_; +} + +void PepperSession::set_receiver_token(const std::string& receiver_token) { + DCHECK(CalledOnValidThread()); + // set_receiver_token() should not be called on the client side. + NOTREACHED(); +} + +void PepperSession::set_shared_secret(const std::string& secret) { + DCHECK(CalledOnValidThread()); + shared_secret_ = secret; +} + +const std::string& PepperSession::shared_secret() { + DCHECK(CalledOnValidThread()); + return shared_secret_; +} + +void PepperSession::Close() { + DCHECK(CalledOnValidThread()); + + if (state_ == CONNECTING || state_ == CONNECTED || + state_ == CONNECTED_CHANNELS) { + // Send session-terminate message. + JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, + session_id_); + scoped_ptr<IqRequest> terminate_request( + session_manager_->CreateIqRequest()); + terminate_request->SendIq(message.ToXml()); + } + + CloseInternal(false); +} + +void PepperSession::OnIncomingMessage(const JingleMessage& message, + JingleMessageReply* reply) { + DCHECK(CalledOnValidThread()); + + if (message.from != peer_jid_) { + // Ignore messages received from a different Jid. + *reply = JingleMessageReply(JingleMessageReply::INVALID_SID); + return; + } + + switch (message.action) { + case JingleMessage::SESSION_ACCEPT: + OnAccept(message, reply); + break; + + case JingleMessage::TRANSPORT_INFO: + ProcessTransportInfo(message); + break; + + case JingleMessage::SESSION_REJECT: + OnReject(message, reply); + break; + + case JingleMessage::SESSION_TERMINATE: + OnTerminate(message, reply); + break; + + default: + *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); + } +} + +void PepperSession::OnAccept(const JingleMessage& message, + JingleMessageReply* reply) { + if (state_ != CONNECTING) { + *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); + return; + } + + if (!InitializeConfigFromDescription(message.description.get())) { + OnError(ERROR_INCOMPATIBLE_PROTOCOL); + return; + } + + CreateChannels(); + SetState(CONNECTED); + + // In case there is transport information in the accept message. + ProcessTransportInfo(message); +} + +void PepperSession::ProcessTransportInfo(const JingleMessage& message) { + for (std::list<cricket::Candidate>::const_iterator it = + message.candidates.begin(); + it != message.candidates.end(); ++it) { + ChannelsMap::iterator channel = channels_.find(it->name()); + if (channel == channels_.end()) { + LOG(WARNING) << "Received candidate for unknown channel " << it->name(); + continue; + } + channel->second->AddRemoveCandidate(*it); + } +} + +void PepperSession::OnReject(const JingleMessage& message, + JingleMessageReply* reply) { + if (state_ != CONNECTING) { + *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); + return; + } + + // TODO(sergeyu): Parse exact rejection reason from reply and pass it + // to OnError(). + OnError(ERROR_SESSION_REJECTED); +} + +void PepperSession::OnTerminate(const JingleMessage& message, + JingleMessageReply* reply) { + if (state_ == CONNECTING) { + // If we are not connected yet, then interpret terminate message + // as rejection. + OnError(ERROR_SESSION_REJECTED); + return; + } + + CloseInternal(false); +} + +bool PepperSession::InitializeConfigFromDescription( + const ContentDescription* description) { + DCHECK(description); + + remote_cert_ = description->certificate(); + if (remote_cert_.empty()) { + LOG(ERROR) << "session-accept does not specify certificate"; + return false; + } + + if (!description->config()->GetFinalConfig(&config_)) { + LOG(ERROR) << "session-accept does not specify configuration"; + return false; + } + if (!candidate_config()->IsSupported(config_)) { + LOG(ERROR) << "session-accept specifies an invalid configuration"; + return false; + } + + return true; +} + +void PepperSession::AddLocalCandidate(const cricket::Candidate& candidate) { + pending_candidates_.push_back(candidate); + + if (!transport_infos_timer_.IsRunning()) { + // Delay sending the new candidates in case we get more candidates + // that we can send in one message. + transport_infos_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs), + this, &PepperSession::SendTransportInfo); + } +} + +void PepperSession::OnDeleteChannel(PepperChannel* channel) { + ChannelsMap::iterator it = channels_.find(channel->name()); + DCHECK_EQ(it->second, channel); + channels_.erase(it); +} + +void PepperSession::SendTransportInfo() { + JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); + message.candidates.swap(pending_candidates_); + scoped_ptr<IqRequest> request(session_manager_->CreateIqRequest()); + request->SendIq(message.ToXml()); +} + +void PepperSession::CreateChannels() { + CreateStreamChannel( + kControlChannelName, + base::Bind(&PepperSession::OnChannelConnected, + base::Unretained(this), &control_channel_socket_)); + CreateStreamChannel( + kEventChannelName, + base::Bind(&PepperSession::OnChannelConnected, + base::Unretained(this), &event_channel_socket_)); +} + +void PepperSession::OnChannelConnected( + scoped_ptr<net::Socket>* socket_container, + net::StreamSocket* socket) { + if (!socket) { + LOG(ERROR) << "Failed to connect control or events channel. " + << "Terminating connection"; + OnError(ERROR_CHANNEL_CONNECTION_FAILURE); + return; + } + + socket_container->reset(socket); + + if (control_channel_socket_.get() && event_channel_socket_.get()) + SetState(CONNECTED_CHANNELS); +} + +void PepperSession::CloseInternal(bool failed) { + DCHECK(CalledOnValidThread()); + + if (state_ != FAILED && state_ != CLOSED) { + control_channel_socket_.reset(); + event_channel_socket_.reset(); + + if (failed) + SetState(FAILED); + else + SetState(CLOSED); + } +} + +void PepperSession::SetState(State new_state) { + DCHECK(CalledOnValidThread()); + + if (new_state != state_) { + DCHECK_NE(state_, CLOSED); + DCHECK_NE(state_, FAILED); + + state_ = new_state; + if (state_change_callback_.get()) + state_change_callback_->Run(new_state); + } +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/pepper_session.h b/remoting/protocol/pepper_session.h new file mode 100644 index 0000000..99b9f6a --- /dev/null +++ b/remoting/protocol/pepper_session.h @@ -0,0 +1,168 @@ +// 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_PEPPER_SESSION_H_ +#define REMOTING_PROTOCOL_PEPPER_SESSION_H_ + +#include <map> +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/timer.h" +#include "crypto/rsa_private_key.h" +#include "net/base/completion_callback.h" +#include "remoting/protocol/jingle_messages.h" +#include "remoting/protocol/session.h" +#include "remoting/protocol/session_config.h" + +namespace net { +class CertVerifier; +class ClientSocketFactory; +class Socket; +class StreamSocket; +class X509Certificate; +} // namespace net + +namespace pp { +class Instance; +} // namespace pp + +namespace remoting { + +class IqRequest; + +namespace protocol { + +class PepperChannel; +class PepperSessionManager; +class SocketWrapper; + +// Implements the protocol::Session interface using the Pepper P2P +// Transport API. Created by PepperSessionManager for incoming and +// outgoing connections. +class PepperSession : public Session { + public: + // TODO(sergeyu): Move this type and error() method to the Session + // interface. + enum Error { + ERROR_NO_ERROR = 0, + ERROR_PEER_IS_OFFLINE, + ERROR_SESSION_REJECTED, + ERROR_INCOMPATIBLE_PROTOCOL, + ERROR_CHANNEL_CONNECTION_FAILURE, + }; + + virtual ~PepperSession(); + + Error error(); + + // 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 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 set_shared_secret(const std::string& secret) OVERRIDE; + virtual const std::string& shared_secret() OVERRIDE; + virtual void Close() OVERRIDE; + + private: + friend class PepperSessionManager; + friend class PepperStreamChannel; + + typedef std::map<std::string, PepperChannel*> ChannelsMap; + + PepperSession(PepperSessionManager* session_manager); + + // Start cs connection by sending session-initiate message. + void StartConnection(const std::string& peer_jid, + const std::string& peer_public_key, + const std::string& client_token, + CandidateSessionConfig* config, + Session::StateChangeCallback* state_change_callback); + + // Handler for session-initiate response. + void OnSessionInitiateResponse(const buzz::XmlElement* response); + + // Called when an error occurs. Sets |error_| and closes the session. + void OnError(Error error); + + // Called by PepperSessionManager on incoming |message|. Must fill + // in |reply|. + void OnIncomingMessage(const JingleMessage& message, + JingleMessageReply* reply); + + // Message handlers for incoming messages. + void OnAccept(const JingleMessage& message, JingleMessageReply* reply); + void OnReject(const JingleMessage& message, JingleMessageReply* reply); + void OnTerminate(const JingleMessage& message, JingleMessageReply* reply); + void ProcessTransportInfo(const JingleMessage& message); + + // Called from OnAccept() to initialize session config. + bool InitializeConfigFromDescription(const ContentDescription* description); + + // Called by PepperChannel. + void AddLocalCandidate(const cricket::Candidate& candidate); + void OnDeleteChannel(PepperChannel* channel); + + void SendTransportInfo(); + + // Helper methods to create event and control channels. + // TODO(sergeyu): Remove these methods. + void CreateChannels(); + void OnChannelConnected(scoped_ptr<net::Socket>* socket_container, + net::StreamSocket* socket); + + // Close all the channels and terminate the session. + void CloseInternal(bool failed); + + // Sets |state_| to |new_state| and calls state change callback. + void SetState(State new_state); + + PepperSessionManager* session_manager_; + std::string peer_jid_; + std::string peer_public_key_; + scoped_ptr<CandidateSessionConfig> candidate_config_; + scoped_ptr<StateChangeCallback> state_change_callback_; + + std::string session_id_; + State state_; + Error error_; + + std::string remote_cert_; + SessionConfig config_; + + std::string shared_secret_; + std::string initiator_token_; + std::string receiver_token_; + + scoped_ptr<IqRequest> initiate_request_; + + ChannelsMap channels_; + + scoped_ptr<net::Socket> control_channel_socket_; + scoped_ptr<net::Socket> event_channel_socket_; + + base::OneShotTimer<PepperSession> transport_infos_timer_; + std::list<cricket::Candidate> pending_candidates_; + + DISALLOW_COPY_AND_ASSIGN(PepperSession); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_PEPPER_SESSION_H_ diff --git a/remoting/protocol/pepper_session_manager.cc b/remoting/protocol/pepper_session_manager.cc new file mode 100644 index 0000000..28d979d --- /dev/null +++ b/remoting/protocol/pepper_session_manager.cc @@ -0,0 +1,146 @@ +// 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/pepper_session_manager.h" + +#include "base/bind.h" +#include "remoting/jingle_glue/jingle_info_request.h" +#include "remoting/jingle_glue/signal_strategy.h" +#include "remoting/protocol/jingle_messages.h" +#include "remoting/protocol/pepper_session.h" +#include "third_party/libjingle/source/talk/base/socketaddress.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" + +using buzz::QName; + +namespace remoting { +namespace protocol { + +PepperSessionManager::PepperSessionManager(pp::Instance* pp_instance) + : pp_instance_(pp_instance), + signal_strategy_(NULL), + listener_(NULL), + allow_nat_traversal_(false) { +} + +PepperSessionManager::~PepperSessionManager() { + Close(); +} + +void PepperSessionManager::Init( + const std::string& local_jid, + SignalStrategy* signal_strategy, + SessionManager::Listener* listener, + crypto::RSAPrivateKey* private_key, + const std::string& certificate, + bool allow_nat_traversal) { + listener_ = listener; + local_jid_ = local_jid; + signal_strategy_ = signal_strategy; + private_key_.reset(private_key); + certificate_ = certificate; + allow_nat_traversal_ = allow_nat_traversal; + + signal_strategy_->SetListener(this); + + // If NAT traversal is enabled then we need to request STUN/Relay info. + if (allow_nat_traversal) { + jingle_info_request_.reset( + new JingleInfoRequest(signal_strategy_->CreateIqRequest(), NULL)); + jingle_info_request_->Send(base::Bind( + &PepperSessionManager::OnJingleInfo, base::Unretained(this))); + } else { + listener_->OnSessionManagerInitialized(); + } +} + +void PepperSessionManager::OnJingleInfo( + const std::string& relay_token, + const std::vector<std::string>& relay_hosts, + const std::vector<talk_base::SocketAddress>& stun_hosts) { + DCHECK(CalledOnValidThread()); + + // TODO(sergeyu): Add support for multiple STUN/relay servers when + // it's implemented in libjingle and P2P Transport API. + transport_config_.stun_server = stun_hosts[0].ToString(); + transport_config_.relay_server = relay_hosts[0]; + transport_config_.relay_token = relay_token; + LOG(INFO) << "STUN server: " << transport_config_.stun_server + << " Relay server: " << transport_config_.relay_server + << " Relay token: " << transport_config_.relay_token; + + listener_->OnSessionManagerInitialized(); +} + +Session* PepperSessionManager::Connect( + const std::string& host_jid, + const std::string& host_public_key, + const std::string& client_token, + CandidateSessionConfig* config, + Session::StateChangeCallback* state_change_callback) { + PepperSession* session = new PepperSession(this); + session->StartConnection(host_jid, host_public_key, client_token, + config, state_change_callback); + sessions_[session->session_id_] = session; + return session; +} + +void PepperSessionManager::Close() { + DCHECK(CalledOnValidThread()); + + // Close() can be called only after all sessions are destroyed. + DCHECK(sessions_.empty()); + + listener_ = NULL; + jingle_info_request_.reset(); + + signal_strategy_->SetListener(NULL); +} + +bool PepperSessionManager::OnIncomingStanza(const buzz::XmlElement* stanza) { + if (!JingleMessage::IsJingleMessage(stanza)) + return false; + + JingleMessage message; + std::string error; + if (!message.ParseXml(stanza, &error)) { + SendReply(stanza, JingleMessageReply(JingleMessageReply::BAD_REQUEST)); + return true; + } + + if (message.action == JingleMessage::SESSION_INITIATE) { + SendReply(stanza, JingleMessageReply( + JingleMessageReply::NOT_IMPLEMENTED, + "Can't accept sessions on the client")); + return true; + } + + SessionsMap::iterator it = sessions_.find(message.sid); + if (it == sessions_.end()) { + SendReply(stanza, JingleMessageReply(JingleMessageReply::INVALID_SID)); + return true; + } + + JingleMessageReply reply; + it->second->OnIncomingMessage(message, &reply); + SendReply(stanza, reply); + return true; +} + +IqRequest* PepperSessionManager::CreateIqRequest() { + return signal_strategy_->CreateIqRequest(); +} + +void PepperSessionManager::SendReply(const buzz::XmlElement* original_stanza, + const JingleMessageReply& reply) { + buzz::XmlElement* stanza = reply.ToXml(original_stanza); + signal_strategy_->SendStanza(stanza); +} + +void PepperSessionManager::SessionDestroyed(PepperSession* session) { + sessions_.erase(session->session_id_); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/pepper_session_manager.h b/remoting/protocol/pepper_session_manager.h new file mode 100644 index 0000000..b8f7166 --- /dev/null +++ b/remoting/protocol/pepper_session_manager.h @@ -0,0 +1,105 @@ +// 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_PEPPER_SESSION_MANAGER_H_ +#define REMOTING_PROTOCOL_PEPPER_SESSION_MANAGER_H_ + +#include <map> +#include <list> +#include <string> + +#include "base/memory/ref_counted.h" +#include "net/base/x509_certificate.h" +#include "remoting/jingle_glue/signal_strategy.h" +#include "remoting/protocol/pepper_channel.h" +#include "remoting/protocol/session_manager.h" +#include "remoting/protocol/transport_config.h" + +namespace pp { +class Instance; +} // namespace pp + +namespace buzz { +class XmlElement; +} // namespace buzz + +namespace talk_base { +class SocketAddress; +} // namespace talk_base + +namespace remoting { + +class JingleInfoRequest; + +namespace protocol { + +struct JingleMessage; +struct JingleMessageReply; +class PepperSession; + +// This class implements SessionManager interface on top of the Pepper +// P2P Transport API. +class PepperSessionManager : public SessionManager, + public SignalStrategy::Listener { + public: + explicit PepperSessionManager(pp::Instance* pp_instance); + virtual ~PepperSessionManager(); + + // SessionManager interface. + virtual void Init(const std::string& local_jid, + SignalStrategy* signal_strategy, + SessionManager::Listener* listener, + crypto::RSAPrivateKey* private_key, + const std::string& certificate, + bool allow_nat_traversal) OVERRIDE; + virtual Session* Connect( + const std::string& host_jid, + const std::string& host_public_key, + const std::string& client_token, + CandidateSessionConfig* config, + Session::StateChangeCallback* state_change_callback) OVERRIDE; + virtual void Close() OVERRIDE; + + // SignalStrategy::Listener interface. + virtual bool OnIncomingStanza(const buzz::XmlElement* stanza) OVERRIDE; + + private: + friend class PepperSession; + + typedef std::map<std::string, PepperSession*> SessionsMap; + + void OnJingleInfo( + const std::string& relay_token, + const std::vector<std::string>& relay_hosts, + const std::vector<talk_base::SocketAddress>& stun_hosts); + + IqRequest* CreateIqRequest(); + void SendReply(const buzz::XmlElement* original_stanza, + const JingleMessageReply& reply); + + // Called by PepperSession when it is being destroyed. + void SessionDestroyed(PepperSession* session); + + pp::Instance* pp_instance_; + + std::string local_jid_; + SignalStrategy* signal_strategy_; + SessionManager::Listener* listener_; + scoped_ptr<crypto::RSAPrivateKey> private_key_; + std::string certificate_; + bool allow_nat_traversal_; + + TransportConfig transport_config_; + + scoped_ptr<JingleInfoRequest> jingle_info_request_; + + SessionsMap sessions_; + + DISALLOW_COPY_AND_ASSIGN(PepperSessionManager); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_PEPPER_SESSION_MANAGER_H_ diff --git a/remoting/protocol/pepper_stream_channel.cc b/remoting/protocol/pepper_stream_channel.cc new file mode 100644 index 0000000..9769adc --- /dev/null +++ b/remoting/protocol/pepper_stream_channel.cc @@ -0,0 +1,264 @@ +// 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/pepper_stream_channel.h" + +#include "base/bind.h" +#include "crypto/hmac.h" +#include "jingle/glue/utils.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/socket/ssl_client_socket.h" +#include "net/socket/client_socket_factory.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/cpp/dev/transport_dev.h" +#include "ppapi/cpp/var.h" +#include "remoting/protocol/channel_authenticator.h" +#include "remoting/protocol/pepper_session.h" +#include "remoting/protocol/transport_config.h" +#include "third_party/libjingle/source/talk/p2p/base/candidate.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; + +// Values for the TCP send and receive buffer size. This should be tuned to +// accomodate high latency network but not backlog the decoding pipeline. +const int kTcpReceiveBufferSize = 256 * 1024; +const int kTcpSendBufferSize = kTcpReceiveBufferSize + 30 * 1024; + +// Helper method to create a SSL client socket. +net::SSLClientSocket* CreateSSLClientSocket( + net::StreamSocket* socket, const std::string& der_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.der_cert = der_cert; + ssl_config.allowed_bad_certs.push_back(cert_and_status); + + // Revocation checking is not needed because we use self-signed + // certs. Disable it so that SSL layer doesn't try to initialize + // OCSP (OCSP works only on IO thread). + ssl_config.rev_checking_enabled = false; + + // SSLClientSocket takes ownership of the |socket|. + net::HostPortPair host_and_port("chromoting", 0); + net::SSLClientSocketContext context; + context.cert_verifier = cert_verifier; + net::SSLClientSocket* ssl_socket = + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + socket, host_and_port, ssl_config, NULL, context); + return ssl_socket; +} + +} // namespace + +PepperStreamChannel::PepperStreamChannel( + PepperSession* session, + const std::string& name, + const Session::StreamChannelCallback& callback) + : session_(session), + name_(name), + callback_(callback), + channel_(NULL), + connected_(false), + ssl_client_socket_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(p2p_connect_callback_( + this, &PepperStreamChannel::OnP2PConnect)), + ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( + this, &PepperStreamChannel::OnSSLConnect)) { +} + +PepperStreamChannel::~PepperStreamChannel() { + // Verify that the |channel_| is ether destroyed or we own it. + DCHECK_EQ(channel_, owned_channel_.get()); + // Channel should be already destroyed if we were connected. + DCHECK(!connected_ || channel_ == NULL); +} + +void PepperStreamChannel::Connect(pp::Instance* pp_instance, + const TransportConfig& transport_config, + const std::string& remote_cert) { + DCHECK(CalledOnValidThread()); + + remote_cert_ = remote_cert; + + pp::Transport_Dev* transport = + new pp::Transport_Dev(pp_instance, name_.c_str(), "tcp"); + + if (transport->SetProperty(PP_TRANSPORTPROPERTY_TCP_RECEIVE_WINDOW, + pp::Var(kTcpReceiveBufferSize)) != PP_OK) { + LOG(ERROR) << "Failed to set TCP receive window"; + } + if (transport->SetProperty(PP_TRANSPORTPROPERTY_TCP_SEND_WINDOW, + pp::Var(kTcpSendBufferSize)) != PP_OK) { + LOG(ERROR) << "Failed to set TCP send window"; + } + + if (transport->SetProperty(PP_TRANSPORTPROPERTY_TCP_NO_DELAY, + pp::Var(true)) != PP_OK) { + LOG(ERROR) << "Failed to set TCP_NODELAY"; + } + + if (transport->SetProperty(PP_TRANSPORTPROPERTY_TCP_ACK_DELAY, + pp::Var(kTcpAckDelayMilliseconds)) != PP_OK) { + LOG(ERROR) << "Failed to set TCP ACK delay."; + } + + if (transport_config.nat_traversal) { + if (transport->SetProperty( + PP_TRANSPORTPROPERTY_STUN_SERVER, + pp::Var(transport_config.stun_server)) != PP_OK) { + LOG(ERROR) << "Failed to set STUN server."; + } + + if (transport->SetProperty( + PP_TRANSPORTPROPERTY_RELAY_SERVER, + pp::Var(transport_config.relay_server)) != PP_OK) { + LOG(ERROR) << "Failed to set Relay server."; + } + + if (transport->SetProperty( + PP_TRANSPORTPROPERTY_RELAY_TOKEN, + pp::Var(transport_config.relay_token)) != PP_OK) { + LOG(ERROR) << "Failed to set Relay token."; + } + } + + channel_ = new PepperTransportSocketAdapter(transport, name_, this); + owned_channel_.reset(channel_); + + int result = channel_->Connect(&p2p_connect_callback_); + if (result != net::ERR_IO_PENDING) + OnP2PConnect(result); +} + +void PepperStreamChannel::AddRemoveCandidate( + const cricket::Candidate& candidate) { + DCHECK(CalledOnValidThread()); + if (channel_) + channel_->AddRemoteCandidate(jingle_glue::SerializeP2PCandidate(candidate)); +} + +const std::string& PepperStreamChannel::name() { + DCHECK(CalledOnValidThread()); + return name_; +} + +void PepperStreamChannel::OnChannelDeleted() { + if (connected_) { + channel_ = NULL; + // The PepperTransportSocketAdapter is being deleted, so delete the + // channel too. + session_->OnDeleteChannel(this); + delete this; + } +} + +void PepperStreamChannel::OnChannelNewLocalCandidate( + const std::string& candidate) { + DCHECK(CalledOnValidThread()); + + cricket::Candidate candidate_value; + if (!jingle_glue::DeserializeP2PCandidate(candidate, &candidate_value)) { + LOG(ERROR) << "Failed to parse candidate " << candidate; + } + session_->AddLocalCandidate(candidate_value); +} + +void PepperStreamChannel::OnP2PConnect(int result) { + DCHECK(CalledOnValidThread()); + + if (result != net::OK || !EstablishSSLConnection()) + NotifyConnectFailed(); +} + +bool PepperStreamChannel::EstablishSSLConnection() { + DCHECK(CalledOnValidThread()); + + cert_verifier_.reset(new net::CertVerifier()); + + // Create client SSL socket. + ssl_client_socket_ = CreateSSLClientSocket( + owned_channel_.release(), remote_cert_, cert_verifier_.get()); + socket_.reset(ssl_client_socket_); + + int result = ssl_client_socket_->Connect(&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 PepperStreamChannel::OnSSLConnect(int result) { + DCHECK(CalledOnValidThread()); + + if (result != net::OK) { + LOG(ERROR) << "Error during SSL connection: " << result; + NotifyConnectFailed(); + return; + } + + DCHECK(socket_->IsConnected()); + AuthenticateChannel(); +} + +void PepperStreamChannel::AuthenticateChannel() { + DCHECK(CalledOnValidThread()); + + authenticator_.reset(new ClientChannelAuthenticator(ssl_client_socket_)); + authenticator_->Authenticate( + session_->shared_secret(), + base::Bind(&PepperStreamChannel::OnAuthenticationDone, + base::Unretained(this))); +} + +void PepperStreamChannel::OnAuthenticationDone( + ChannelAuthenticator::Result result) { + DCHECK(CalledOnValidThread()); + + switch (result) { + case ChannelAuthenticator::SUCCESS: + NotifyConnected(socket_.release()); + break; + + case ChannelAuthenticator::FAILURE: + NotifyConnectFailed(); + break; + } +} + +void PepperStreamChannel::NotifyConnected(net::StreamSocket* socket) { + DCHECK(!connected_); + callback_.Run(socket); + connected_ = true; +} + +void PepperStreamChannel::NotifyConnectFailed() { + channel_ = NULL; + owned_channel_.reset(); + socket_.reset(); + + NotifyConnected(NULL); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/pepper_stream_channel.h b/remoting/protocol/pepper_stream_channel.h new file mode 100644 index 0000000..d5afe27 --- /dev/null +++ b/remoting/protocol/pepper_stream_channel.h @@ -0,0 +1,92 @@ +// 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_PEPPER_STREAM_CHANNEL_H_ +#define REMOTING_PROTOCOL_PEPPER_STREAM_CHANNEL_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "remoting/protocol/channel_authenticator.h" +#include "remoting/protocol/pepper_channel.h" +#include "remoting/protocol/pepper_transport_socket_adapter.h" +#include "remoting/protocol/session.h" + +namespace net { +class CertVerifier; +class StreamSocket; +class SSLClientSocket; +class SSLServerSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +class PepperSession; + +class PepperStreamChannel : public PepperChannel, + public PepperTransportSocketAdapter::Observer { + public: + PepperStreamChannel(PepperSession* session, + const std::string& name, + const Session::StreamChannelCallback& callback); + virtual ~PepperStreamChannel(); + + // PepperChannel implementation. + virtual void Connect(pp::Instance* pp_instance, + const TransportConfig& transport_config, + const std::string& remote_cert) OVERRIDE; + virtual void AddRemoveCandidate(const cricket::Candidate& candidate) OVERRIDE; + virtual const std::string& name() OVERRIDE; + + // PepperTransportSocketAdapter implementation. + virtual void OnChannelDeleted() OVERRIDE; + virtual void OnChannelNewLocalCandidate( + const std::string& candidate) OVERRIDE; + + private: + void OnP2PConnect(int result); + + bool EstablishSSLConnection(); + void OnSSLConnect(int result); + + void AuthenticateChannel(); + void OnAuthenticationDone(ChannelAuthenticator::Result result); + + void NotifyConnected(net::StreamSocket* socket); + void NotifyConnectFailed(); + + PepperSession* session_; + std::string name_; + Session::StreamChannelCallback callback_; + + std::string remote_cert_; + + // We own |channel_| until it is connected. After that + // SSLClientSocket owns it. + scoped_ptr<PepperTransportSocketAdapter> owned_channel_; + PepperTransportSocketAdapter* channel_; + + // Indicates that we've finished connecting. + bool connected_; + + scoped_ptr<net::StreamSocket> socket_; + net::SSLClientSocket* ssl_client_socket_; + + // Used to verify the certificate received in SSLClientSocket. + scoped_ptr<net::CertVerifier> cert_verifier_; + + scoped_ptr<ChannelAuthenticator> authenticator_; + + // Callback called by the TCP and SSL layers. + net::CompletionCallbackImpl<PepperStreamChannel> p2p_connect_callback_; + net::CompletionCallbackImpl<PepperStreamChannel> ssl_connect_callback_; + + DISALLOW_COPY_AND_ASSIGN(PepperStreamChannel); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_PEPPER_STREAM_CHANNEL_H_ diff --git a/remoting/protocol/pepper_transport_socket_adapter.cc b/remoting/protocol/pepper_transport_socket_adapter.cc index d247e05..1d65be0 100644 --- a/remoting/protocol/pepper_transport_socket_adapter.cc +++ b/remoting/protocol/pepper_transport_socket_adapter.cc @@ -5,8 +5,10 @@ #include "remoting/protocol/pepper_transport_socket_adapter.h" #include "base/logging.h" +#include "net/base/address_list.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/base/net_util.h" #include "ppapi/c/pp_errors.h" #include "ppapi/cpp/dev/transport_dev.h" #include "ppapi/cpp/var.h" @@ -39,18 +41,17 @@ int PPErrorToNetError(int result) { } // namespace PepperTransportSocketAdapter::PepperTransportSocketAdapter( - pp::Instance* pp_instance, + pp::Transport_Dev* transport, const std::string& name, Observer* observer) : name_(name), observer_(observer), + transport_(transport), connected_(false), get_address_pending_(false), read_callback_(NULL), write_callback_(NULL) { callback_factory_.Initialize(this); - transport_.reset(new pp::Transport_Dev( - pp_instance, name_.c_str(), kTcpProtocol)); } PepperTransportSocketAdapter::~PepperTransportSocketAdapter() { @@ -148,8 +149,11 @@ bool PepperTransportSocketAdapter::IsConnectedAndIdle() const { int PepperTransportSocketAdapter::GetPeerAddress( net::AddressList* address) const { - NOTIMPLEMENTED(); - return net::ERR_FAILED; + // We don't have a meaningful peer address, but we can't return an + // error, so we return a INADDR_ANY instead. + net::IPAddressNumber ip_address(4); + *address = net::AddressList::CreateFromIPAddress(ip_address, 0); + return net::OK; } int PepperTransportSocketAdapter::GetLocalAddress( diff --git a/remoting/protocol/pepper_transport_socket_adapter.h b/remoting/protocol/pepper_transport_socket_adapter.h index c69f8d2..92cc410 100644 --- a/remoting/protocol/pepper_transport_socket_adapter.h +++ b/remoting/protocol/pepper_transport_socket_adapter.h @@ -38,7 +38,7 @@ class PepperTransportSocketAdapter : public base::NonThreadSafe, virtual void OnChannelNewLocalCandidate(const std::string& candidate) = 0; }; - PepperTransportSocketAdapter(pp::Instance* pp_instance, + PepperTransportSocketAdapter(pp::Transport_Dev* transport, const std::string& name, Observer* observer); virtual ~PepperTransportSocketAdapter(); diff --git a/remoting/protocol/transport_config.cc b/remoting/protocol/transport_config.cc new file mode 100644 index 0000000..bd2be68 --- /dev/null +++ b/remoting/protocol/transport_config.cc @@ -0,0 +1,18 @@ +// 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/transport_config.h" + +namespace remoting { +namespace protocol { + +TransportConfig::TransportConfig() + : nat_traversal(false) { +} + +TransportConfig::~TransportConfig() { +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/transport_config.h b/remoting/protocol/transport_config.h new file mode 100644 index 0000000..c1105ef --- /dev/null +++ b/remoting/protocol/transport_config.h @@ -0,0 +1,26 @@ +// 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_TRANSPORT_CONFIG_H_ +#define REMOTING_PROTOCOL_TRANSPORT_CONFIG_H_ + +#include <string> + +namespace remoting { +namespace protocol { + +struct TransportConfig { + TransportConfig(); + ~TransportConfig(); + + bool nat_traversal; + std::string stun_server; + std::string relay_server; + std::string relay_token; +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_TRANSPORT_CONFIG_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 01c4d39..8d9444b 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -708,6 +708,13 @@ 'protocol/message_decoder.h', 'protocol/message_reader.cc', 'protocol/message_reader.h', + 'protocol/pepper_channel.h', + 'protocol/pepper_session.cc', + 'protocol/pepper_session.h', + 'protocol/pepper_session_manager.cc', + 'protocol/pepper_session_manager.h', + 'protocol/pepper_stream_channel.cc', + 'protocol/pepper_stream_channel.h', 'protocol/pepper_transport_socket_adapter.cc', 'protocol/pepper_transport_socket_adapter.h', 'protocol/protobuf_video_reader.cc', @@ -734,6 +741,8 @@ 'protocol/session_manager.h', 'protocol/socket_reader_base.cc', 'protocol/socket_reader_base.h', + 'protocol/transport_config.cc', + 'protocol/transport_config.h', 'protocol/util.cc', 'protocol/util.h', 'protocol/video_reader.cc', |