Chromium Code Reviews| Index: remoting/protocol/pepper_stream_channel.cc |
| diff --git a/remoting/protocol/pepper_stream_channel.cc b/remoting/protocol/pepper_stream_channel.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b51b2b1a2521bb4da86ae449fca6bfd43a0b0404 |
| --- /dev/null |
| +++ b/remoting/protocol/pepper_stream_channel.cc |
| @@ -0,0 +1,242 @@ |
| +// 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_nss.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 "third_party/libjingle/source/talk/p2p/base/candidate.h" |
| + |
| +namespace remoting { |
| +namespace protocol { |
| + |
| +namespace { |
| + |
| +const char kTcpProtocol[] = "tcp"; |
| + |
| +// 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(const TransportConfig& transport_config, |
| + const std::string& remote_cert) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + remote_cert_ = remote_cert; |
| + |
| + pp::Transport_Dev* transport = |
| + new pp::Transport_Dev(session_->pp_instance(), name_.c_str(), |
| + kTcpProtocol); |
| + |
| + 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"; |
| + } |
| + |
| + // TODO(sergeyu): Add properties for ACK delay and Neagle in the |
|
Wez
2011/09/01 01:08:29
nit: Neagle -> TCP_NODELAY
Sergey Ulanov
2011/09/08 00:48:36
TODO is done now.
|
| + // Transport API and set them here. |
| + |
| + 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."; |
| + } |
| + } |
| + |
| + 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 |