| 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..9769adcd74c4682d391649248cae1e1e99c77378
|
| --- /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
|
|
|