Allow TransportController to create a QuicTransportChannel

A QuicTransport is implemented that subclasses Transport
and takes ownership of the QuicTransportChannel/P2PTransportChannel.

Split from CL https://codereview.webrtc.org/1844803002/.

BUG=

Review-Url: https://codereview.webrtc.org/1856943002
Cr-Commit-Position: refs/heads/master@{#12575}
This commit is contained in:
mikescarlett 2016-04-29 20:20:54 -07:00 committed by Commit bot
parent 9bc517f123
commit e7748674ee
16 changed files with 678 additions and 118 deletions

View File

@ -85,7 +85,7 @@ std::string SSLFingerprint::GetRfc4572Fingerprint() const {
return fingerprint;
}
std::string SSLFingerprint::ToString() {
std::string SSLFingerprint::ToString() const {
std::string fp_str = algorithm;
fp_str.append(" ");
fp_str.append(GetRfc4572Fingerprint());

View File

@ -41,7 +41,7 @@ struct SSLFingerprint {
std::string GetRfc4572Fingerprint() const;
std::string ToString();
std::string ToString() const;
std::string algorithm;
rtc::CopyOnWriteBuffer digest;

View File

@ -66,27 +66,11 @@ class DtlsTransport : public Base {
rtc::SSLFingerprint* local_fp =
Base::local_description()->identity_fingerprint.get();
if (local_fp) {
// Sanity check local fingerprint.
if (certificate_) {
std::unique_ptr<rtc::SSLFingerprint> local_fp_tmp(
rtc::SSLFingerprint::Create(local_fp->algorithm,
certificate_->identity()));
ASSERT(local_fp_tmp.get() != NULL);
if (!(*local_fp_tmp == *local_fp)) {
std::ostringstream desc;
desc << "Local fingerprint does not match identity. Expected: ";
desc << local_fp_tmp->ToString();
desc << " Got: " << local_fp->ToString();
return BadTransportDescription(desc.str(), error_desc);
}
} else {
return BadTransportDescription(
"Local fingerprint provided but no identity available.",
error_desc);
}
} else {
if (!local_fp) {
certificate_ = nullptr;
} else if (!Base::VerifyCertificateFingerprint(certificate_.get(), local_fp,
error_desc)) {
return false;
}
if (!channel->SetLocalCertificate(certificate_)) {
@ -105,96 +89,23 @@ class DtlsTransport : public Base {
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
rtc::SSLFingerprint* local_fp =
Base::local_description()->identity_fingerprint.get();
rtc::SSLFingerprint* remote_fp =
Base::remote_description()->identity_fingerprint.get();
if (remote_fp && local_fp) {
remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
// From RFC 4145, section-4.1, The following are the values that the
// 'setup' attribute can take in an offer/answer exchange:
// Offer Answer
// ________________
// active passive / holdconn
// passive active / holdconn
// actpass active / passive / holdconn
// holdconn holdconn
//
// Set the role that is most conformant with RFC 5763, Section 5, bullet 1
// The endpoint MUST use the setup attribute defined in [RFC4145].
// The endpoint that is the offerer MUST use the setup attribute
// value of setup:actpass and be prepared to receive a client_hello
// before it receives the answer. The answerer MUST use either a
// setup attribute value of setup:active or setup:passive. Note that
// if the answerer uses setup:passive, then the DTLS handshake will
// not begin until the answerer is received, which adds additional
// latency. setup:active allows the answer and the DTLS handshake to
// occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
// party is active MUST initiate a DTLS handshake by sending a
// ClientHello over each flow (host/port quartet).
// IOW - actpass and passive modes should be treated as server and
// active as client.
ConnectionRole local_connection_role =
Base::local_description()->connection_role;
ConnectionRole remote_connection_role =
Base::remote_description()->connection_role;
bool is_remote_server = false;
if (local_role == CA_OFFER) {
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.",
error_desc);
}
if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
remote_connection_role == CONNECTIONROLE_PASSIVE ||
remote_connection_role == CONNECTIONROLE_NONE) {
is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If remote is NONE or ACTIVE it will act as client.
} else {
if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
remote_connection_role != CONNECTIONROLE_NONE) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.",
error_desc);
}
if (local_connection_role == CONNECTIONROLE_ACTIVE ||
local_connection_role == CONNECTIONROLE_PASSIVE) {
is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If local is passive, local will act as server.
if (!Base::NegotiateRole(local_role, &secure_role_, error_desc)) {
return false;
}
secure_role_ = is_remote_server ? rtc::SSL_CLIENT :
rtc::SSL_SERVER;
} else if (local_fp && (local_role == CA_ANSWER)) {
return BadTransportDescription(
"Local fingerprint supplied when caller didn't offer DTLS.",
error_desc);
} else {
// We are not doing DTLS
remote_fingerprint_.reset(new rtc::SSLFingerprint(
"", NULL, 0));
remote_fingerprint_.reset(new rtc::SSLFingerprint("", nullptr, 0));
}
// Now run the negotiation for the base class.
return Base::NegotiateTransportDescription(local_role, error_desc);
}

View File

@ -406,6 +406,8 @@ class FakeTransport : public Transport {
using Transport::local_description;
using Transport::remote_description;
using Transport::VerifyCertificateFingerprint;
using Transport::NegotiateRole;
protected:
TransportChannelImpl* CreateTransportChannel(int component) override {

View File

@ -401,4 +401,107 @@ bool Transport::NegotiateTransportDescription(ContentAction local_role,
return true;
}
bool Transport::VerifyCertificateFingerprint(
const rtc::RTCCertificate* certificate,
const rtc::SSLFingerprint* fingerprint,
std::string* error_desc) const {
if (!fingerprint) {
return BadTransportDescription("No fingerprint.", error_desc);
}
if (!certificate) {
return BadTransportDescription(
"Fingerprint provided but no identity available.", error_desc);
}
rtc::scoped_ptr<rtc::SSLFingerprint> fp_tmp(rtc::SSLFingerprint::Create(
fingerprint->algorithm, certificate->identity()));
ASSERT(fp_tmp.get() != NULL);
if (*fp_tmp == *fingerprint) {
return true;
}
std::ostringstream desc;
desc << "Local fingerprint does not match identity. Expected: ";
desc << fp_tmp->ToString();
desc << " Got: " << fingerprint->ToString();
return BadTransportDescription(desc.str(), error_desc);
}
bool Transport::NegotiateRole(ContentAction local_role,
rtc::SSLRole* ssl_role,
std::string* error_desc) const {
RTC_DCHECK(ssl_role);
if (!local_description() || !remote_description()) {
const std::string msg =
"Local and Remote description must be set before "
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
// From RFC 4145, section-4.1, The following are the values that the
// 'setup' attribute can take in an offer/answer exchange:
// Offer Answer
// ________________
// active passive / holdconn
// passive active / holdconn
// actpass active / passive / holdconn
// holdconn holdconn
//
// Set the role that is most conformant with RFC 5763, Section 5, bullet 1
// The endpoint MUST use the setup attribute defined in [RFC4145].
// The endpoint that is the offerer MUST use the setup attribute
// value of setup:actpass and be prepared to receive a client_hello
// before it receives the answer. The answerer MUST use either a
// setup attribute value of setup:active or setup:passive. Note that
// if the answerer uses setup:passive, then the DTLS handshake will
// not begin until the answerer is received, which adds additional
// latency. setup:active allows the answer and the DTLS handshake to
// occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
// party is active MUST initiate a DTLS handshake by sending a
// ClientHello over each flow (host/port quartet).
// IOW - actpass and passive modes should be treated as server and
// active as client.
ConnectionRole local_connection_role = local_description()->connection_role;
ConnectionRole remote_connection_role = remote_description()->connection_role;
bool is_remote_server = false;
if (local_role == CA_OFFER) {
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.", error_desc);
}
if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
remote_connection_role == CONNECTIONROLE_PASSIVE ||
remote_connection_role == CONNECTIONROLE_NONE) {
is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If remote is NONE or ACTIVE it will act as client.
} else {
if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
remote_connection_role != CONNECTIONROLE_NONE) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.", error_desc);
}
if (local_connection_role == CONNECTIONROLE_ACTIVE ||
local_connection_role == CONNECTIONROLE_PASSIVE) {
is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If local is passive, local will act as server.
}
*ssl_role = is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER;
return true;
}
} // namespace cricket

View File

@ -315,6 +315,20 @@ class Transport : public sigslot::has_slots<> {
TransportChannelImpl* channel,
std::string* error_desc);
// Returns false if the certificate's identity does not match the fingerprint,
// or either is NULL.
virtual bool VerifyCertificateFingerprint(
const rtc::RTCCertificate* certificate,
const rtc::SSLFingerprint* fingerprint,
std::string* error_desc) const;
// Negotiates the SSL role based off the offer and answer as specified by
// RFC 4145, section-4.1. Returns false if the SSL role cannot be determined
// from the local description and remote description.
virtual bool NegotiateRole(ContentAction local_role,
rtc::SSLRole* ssl_role,
std::string* error_desc) const;
private:
// If a candidate is not acceptable, returns false and sets error.
// Call this before calling OnRemoteCandidates.

View File

@ -235,3 +235,180 @@ TEST_F(TransportTest, TestGetStats) {
EXPECT_EQ(1, stats.channel_stats[0].component);
}
// Tests that VerifyCertificateFingerprint only returns true when the
// certificate matches the fingerprint.
TEST_F(TransportTest, TestVerifyCertificateFingerprint) {
std::string error_desc;
EXPECT_FALSE(
transport_->VerifyCertificateFingerprint(nullptr, nullptr, &error_desc));
rtc::KeyType key_types[] = {rtc::KT_RSA, rtc::KT_ECDSA};
for (auto& key_type : key_types) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", key_type)));
ASSERT_NE(nullptr, certificate);
std::string digest_algorithm;
ASSERT_TRUE(certificate->ssl_certificate().GetSignatureDigestAlgorithm(
&digest_algorithm));
ASSERT_FALSE(digest_algorithm.empty());
rtc::scoped_ptr<rtc::SSLFingerprint> good_fingerprint(
rtc::SSLFingerprint::Create(digest_algorithm, certificate->identity()));
ASSERT_NE(nullptr, good_fingerprint);
EXPECT_TRUE(transport_->VerifyCertificateFingerprint(
certificate.get(), good_fingerprint.get(), &error_desc));
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
certificate.get(), nullptr, &error_desc));
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
nullptr, good_fingerprint.get(), &error_desc));
rtc::SSLFingerprint bad_fingerprint = *good_fingerprint;
bad_fingerprint.digest.AppendData("0", 1);
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
certificate.get(), &bad_fingerprint, &error_desc));
}
}
// Tests that NegotiateRole sets the SSL role correctly.
TEST_F(TransportTest, TestNegotiateRole) {
TransportDescription local_desc(kIceUfrag1, kIcePwd1);
TransportDescription remote_desc(kIceUfrag2, kIcePwd2);
struct NegotiateRoleParams {
cricket::ConnectionRole local_role;
cricket::ConnectionRole remote_role;
cricket::ContentAction local_action;
cricket::ContentAction remote_action;
};
rtc::SSLRole ssl_role;
std::string error_desc;
// Parameters which set the SSL role to SSL_CLIENT.
NegotiateRoleParams valid_client_params[] = {
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER}};
for (auto& param : valid_client_params) {
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_action, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_action, nullptr));
EXPECT_TRUE(
transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
EXPECT_EQ(rtc::SSL_CLIENT, ssl_role);
}
// Parameters which set the SSL role to SSL_SERVER.
NegotiateRoleParams valid_server_params[] = {
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER}};
for (auto& param : valid_server_params) {
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_action, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_action, nullptr));
EXPECT_TRUE(
transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
EXPECT_EQ(rtc::SSL_SERVER, ssl_role);
}
// Invalid parameters due to both peers having a duplicate role.
NegotiateRoleParams duplicate_params[] = {
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_OFFER, cricket::CA_PRANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER}};
for (auto& param : duplicate_params) {
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_action, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_action, nullptr));
EXPECT_FALSE(
transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
}
// Invalid parameters due to the offerer not using ACTPASS.
NegotiateRoleParams offerer_without_actpass_params[] = {
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_ANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_PRANSWER, cricket::CA_OFFER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_OFFER, cricket::CA_ANSWER},
{cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
cricket::CA_OFFER, cricket::CA_PRANSWER},
{cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
cricket::CA_OFFER, cricket::CA_PRANSWER}};
for (auto& param : offerer_without_actpass_params) {
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_action, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_action, nullptr));
EXPECT_FALSE(
transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
}
}

View File

@ -20,6 +20,10 @@
#include "webrtc/p2p/base/p2ptransport.h"
#include "webrtc/p2p/base/port.h"
#ifdef HAVE_QUIC
#include "webrtc/p2p/quic/quictransport.h"
#endif // HAVE_QUIC
namespace cricket {
enum {
@ -219,6 +223,11 @@ Transport* TransportController::CreateTransport_w(
const std::string& transport_name) {
RTC_DCHECK(worker_thread_->IsCurrent());
#ifdef HAVE_QUIC
if (quic_) {
return new QuicTransport(transport_name, port_allocator(), certificate_);
}
#endif // HAVE_QUIC
Transport* transport = new DtlsTransport<P2PTransport>(
transport_name, port_allocator(), certificate_);
return transport;

View File

@ -91,6 +91,9 @@ class TransportController : public sigslot::has_slots<>,
virtual void DestroyTransportChannel_w(const std::string& transport_name,
int component);
void use_quic() { quic_ = true; }
bool quic() const { return quic_; }
// All of these signals are fired on the signalling thread.
// If any transport failed => failed,
@ -222,6 +225,8 @@ class TransportController : public sigslot::has_slots<>,
uint64_t ice_tiebreaker_ = rtc::CreateRandomId64();
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
rtc::AsyncInvoker invoker_;
// True if QUIC is used instead of DTLS.
bool quic_ = false;
};
} // namespace cricket

View File

@ -107,6 +107,8 @@
'quic/quicconnectionhelper.h',
'quic/quicsession.cc',
'quic/quicsession.h',
'quic/quictransport.cc',
'quic/quictransport.h',
'quic/quictransportchannel.cc',
'quic/quictransportchannel.h',
'quic/reliablequicstream.cc',
@ -182,6 +184,7 @@
'sources': [
'quic/quicconnectionhelper_unittest.cc',
'quic/quicsession_unittest.cc',
'quic/quictransport_unittest.cc',
'quic/quictransportchannel_unittest.cc',
'quic/reliablequicstream_unittest.cc',
],

View File

@ -0,0 +1,114 @@
/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/p2p/quic/quictransport.h"
#include "webrtc/p2p/base/p2ptransportchannel.h"
namespace cricket {
QuicTransport::QuicTransport(
const std::string& name,
PortAllocator* allocator,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
: Transport(name, allocator), local_certificate_(certificate) {}
QuicTransport::~QuicTransport() {
DestroyAllChannels();
}
void QuicTransport::SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
local_certificate_ = certificate;
}
bool QuicTransport::GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
if (!local_certificate_) {
return false;
}
*certificate = local_certificate_;
return true;
}
bool QuicTransport::ApplyLocalTransportDescription(
TransportChannelImpl* channel,
std::string* error_desc) {
rtc::SSLFingerprint* local_fp =
local_description()->identity_fingerprint.get();
if (!VerifyCertificateFingerprint(local_certificate_.get(), local_fp,
error_desc)) {
return false;
}
if (!channel->SetLocalCertificate(local_certificate_)) {
return BadTransportDescription("Failed to set local identity.", error_desc);
}
return Transport::ApplyLocalTransportDescription(channel, error_desc);
}
bool QuicTransport::NegotiateTransportDescription(ContentAction action,
std::string* error_desc) {
if (!local_description() || !remote_description()) {
const std::string msg =
"Local and Remote description must be set before "
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
rtc::SSLFingerprint* local_fp =
local_description()->identity_fingerprint.get();
rtc::SSLFingerprint* remote_fp =
remote_description()->identity_fingerprint.get();
if (!local_fp || !remote_fp) {
return BadTransportDescription("Fingerprints must be supplied for QUIC.",
error_desc);
}
remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
if (!NegotiateRole(action, &local_role_, error_desc)) {
return false;
}
// Now run the negotiation for the Transport class.
return Transport::NegotiateTransportDescription(action, error_desc);
}
QuicTransportChannel* QuicTransport::CreateTransportChannel(int component) {
P2PTransportChannel* ice_channel =
new P2PTransportChannel(name(), component, port_allocator());
return new QuicTransportChannel(ice_channel);
}
void QuicTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
delete channel;
}
bool QuicTransport::GetSslRole(rtc::SSLRole* ssl_role) const {
ASSERT(ssl_role != NULL);
*ssl_role = local_role_;
return true;
}
bool QuicTransport::ApplyNegotiatedTransportDescription(
TransportChannelImpl* channel,
std::string* error_desc) {
// Set ssl role and remote fingerprint. These are required for QUIC setup.
if (!channel->SetSslRole(local_role_)) {
return BadTransportDescription("Failed to set ssl role for the channel.",
error_desc);
}
// Apply remote fingerprint.
if (!channel->SetRemoteFingerprint(
remote_fingerprint_->algorithm,
reinterpret_cast<const uint8_t*>(remote_fingerprint_->digest.data()),
remote_fingerprint_->digest.size())) {
return BadTransportDescription("Failed to apply remote fingerprint.",
error_desc);
}
return Transport::ApplyNegotiatedTransportDescription(channel, error_desc);
}
} // namespace cricket

View File

@ -0,0 +1,63 @@
/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_P2P_QUIC_QUICTRANSPORT_H_
#define WEBRTC_P2P_QUIC_QUICTRANSPORT_H_
#include <string>
#include <map>
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/quic/quictransportchannel.h"
namespace cricket {
class P2PTransportChannel;
class PortAllocator;
// TODO(mikescarlett): Refactor to avoid code duplication with DtlsTransport.
class QuicTransport : public Transport {
public:
QuicTransport(const std::string& name,
PortAllocator* allocator,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
~QuicTransport() override;
// Transport overrides.
void SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
bool GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override;
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
return true; // Not needed by QUIC
}
bool GetSslRole(rtc::SSLRole* ssl_role) const override;
protected:
// Transport overrides.
QuicTransportChannel* CreateTransportChannel(int component) override;
void DestroyTransportChannel(TransportChannelImpl* channel) override;
bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
std::string* error_desc) override;
bool NegotiateTransportDescription(ContentAction action,
std::string* error_desc) override;
bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel,
std::string* error_desc) override;
private:
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
rtc::SSLRole local_role_ = rtc::SSL_CLIENT;
rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint_;
};
} // namespace cricket
#endif // WEBRTC_P2P_QUIC_QUICTRANSPORT_H_

View File

@ -0,0 +1,159 @@
/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/p2p/quic/quictransport.h"
#include <string>
#include <vector>
#include "webrtc/base/gunit.h"
#include "webrtc/base/rtccertificate.h"
#include "webrtc/base/sslidentity.h"
using cricket::TransportChannelImpl;
using cricket::QuicTransport;
using cricket::Transport;
using cricket::TransportDescription;
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
static rtc::scoped_refptr<rtc::RTCCertificate> CreateCertificate(
std::string name) {
return rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate(name, rtc::KT_DEFAULT)));
}
static rtc::scoped_ptr<rtc::SSLFingerprint> CreateFingerprint(
rtc::RTCCertificate* cert) {
std::string digest_algorithm;
cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm);
return rtc::scoped_ptr<rtc::SSLFingerprint>(
rtc::SSLFingerprint::Create(digest_algorithm, cert->identity()));
}
class QuicTransportTest : public testing::Test {
public:
QuicTransportTest() : transport_("testing", nullptr, nullptr) {}
void SetTransportDescription(cricket::ConnectionRole local_role,
cricket::ConnectionRole remote_role,
cricket::ContentAction local_action,
cricket::ContentAction remote_action,
rtc::SSLRole expected_ssl_role) {
TransportChannelImpl* channel = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel);
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
CreateCertificate("local"));
ASSERT_NE(nullptr, local_certificate);
transport_.SetLocalCertificate(local_certificate);
rtc::scoped_ptr<rtc::SSLFingerprint> local_fingerprint =
CreateFingerprint(local_certificate.get());
ASSERT_NE(nullptr, local_fingerprint);
TransportDescription local_desc(std::vector<std::string>(), kIceUfrag1,
kIcePwd1, cricket::ICEMODE_FULL, local_role,
local_fingerprint.get());
ASSERT_TRUE(transport_.SetLocalTransportDescription(local_desc,
local_action, nullptr));
// The certificate is applied to QuicTransportChannel when the local
// description is set.
rtc::scoped_refptr<rtc::RTCCertificate> channel_local_certificate =
channel->GetLocalCertificate();
ASSERT_NE(nullptr, channel_local_certificate);
EXPECT_EQ(local_certificate, channel_local_certificate);
rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint =
CreateFingerprint(CreateCertificate("remote").get());
// NegotiateTransportDescription was not called yet. The SSL role should
// not be set and neither should the remote fingerprint.
rtc::scoped_ptr<rtc::SSLRole> role(new rtc::SSLRole());
EXPECT_FALSE(channel->GetSslRole(role.get()));
// Setting the remote description should set the SSL role.
ASSERT_NE(nullptr, remote_fingerprint);
TransportDescription remote_desc(std::vector<std::string>(), kIceUfrag2,
kIcePwd2, cricket::ICEMODE_FULL,
remote_role, remote_fingerprint.get());
ASSERT_TRUE(transport_.SetRemoteTransportDescription(
remote_desc, remote_action, nullptr));
ASSERT_TRUE(channel->GetSslRole(role.get()));
// SSL role should be client because the remote description is an ANSWER.
EXPECT_EQ(expected_ssl_role, *role);
}
protected:
QuicTransport transport_;
};
// Test setting the local certificate.
TEST_F(QuicTransportTest, SetLocalCertificate) {
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
CreateCertificate("local"));
ASSERT_NE(nullptr, local_certificate);
rtc::scoped_refptr<rtc::RTCCertificate> transport_local_certificate;
EXPECT_FALSE(transport_.GetLocalCertificate(&transport_local_certificate));
transport_.SetLocalCertificate(local_certificate);
ASSERT_TRUE(transport_.GetLocalCertificate(&transport_local_certificate));
ASSERT_NE(nullptr, transport_local_certificate);
EXPECT_EQ(local_certificate, transport_local_certificate);
}
// Test setting the ICE role.
TEST_F(QuicTransportTest, SetIceRole) {
TransportChannelImpl* channel1 = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel1);
transport_.SetIceRole(cricket::ICEROLE_CONTROLLING);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_.ice_role());
TransportChannelImpl* channel2 = transport_.CreateChannel(2);
ASSERT_NE(nullptr, channel2);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
}
// Test setting the ICE tie breaker.
TEST_F(QuicTransportTest, SetIceTiebreaker) {
transport_.SetIceTiebreaker(1u);
EXPECT_EQ(1u, transport_.IceTiebreaker());
}
// Test setting the local and remote descriptions for a SSL client.
TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionClient) {
SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER,
cricket::CA_ANSWER, rtc::SSL_CLIENT);
}
// Test setting the local and remote descriptions for a SSL server.
TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionServer) {
SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER,
cricket::CA_ANSWER, rtc::SSL_SERVER);
}
// Test creation and destruction of channels.
TEST_F(QuicTransportTest, CreateAndDestroyChannels) {
TransportChannelImpl* channel1 = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel1);
EXPECT_TRUE(transport_.HasChannel(1));
EXPECT_EQ(channel1, transport_.GetChannel(1));
TransportChannelImpl* channel2 = transport_.CreateChannel(2);
ASSERT_NE(nullptr, channel2);
EXPECT_TRUE(transport_.HasChannel(2));
EXPECT_EQ(channel2, transport_.GetChannel(2));
transport_.DestroyChannel(1);
EXPECT_FALSE(transport_.HasChannel(1));
EXPECT_EQ(nullptr, transport_.GetChannel(1));
transport_.DestroyChannel(2);
EXPECT_FALSE(transport_.HasChannel(2));
EXPECT_EQ(nullptr, transport_.GetChannel(2));
}

View File

@ -274,7 +274,7 @@ int QuicTransportChannel::SendPacket(const char* data,
// |channel_| again.
void QuicTransportChannel::OnWritableState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
LOG_J(LS_VERBOSE, this)
<< "QuicTransportChannel: channel writable state changed to "
<< channel_->writable();
@ -308,7 +308,7 @@ void QuicTransportChannel::OnWritableState(TransportChannel* channel) {
void QuicTransportChannel::OnReceivingState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
LOG_J(LS_VERBOSE, this)
<< "QuicTransportChannel: channel receiving state changed to "
<< channel_->receiving();
@ -324,7 +324,7 @@ void QuicTransportChannel::OnReadPacket(TransportChannel* channel,
const rtc::PacketTime& packet_time,
int flags) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
ASSERT(flags == 0);
switch (quic_state_) {
@ -371,24 +371,24 @@ void QuicTransportChannel::OnReadyToSend(TransportChannel* channel) {
}
void QuicTransportChannel::OnGatheringState(TransportChannelImpl* channel) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalGatheringState(this);
}
void QuicTransportChannel::OnCandidateGathered(TransportChannelImpl* channel,
const Candidate& c) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalCandidateGathered(this, c);
}
void QuicTransportChannel::OnRoleConflict(TransportChannelImpl* channel) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalRoleConflict(this);
}
void QuicTransportChannel::OnRouteChange(TransportChannel* channel,
const Candidate& candidate) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalRouteChange(this, candidate);
}
@ -396,13 +396,13 @@ void QuicTransportChannel::OnSelectedCandidatePairChanged(
TransportChannel* channel,
CandidatePairInterface* selected_candidate_pair,
int last_sent_packet_id) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalSelectedCandidatePairChanged(this, selected_candidate_pair,
last_sent_packet_id);
}
void QuicTransportChannel::OnConnectionRemoved(TransportChannelImpl* channel) {
ASSERT(channel == channel_);
ASSERT(channel == channel_.get());
SignalConnectionRemoved(this);
}

View File

@ -273,7 +273,7 @@ class QuicTransportChannel : public TransportChannelImpl,
rtc::Thread* worker_thread_;
// Underlying channel which is responsible for connecting with the remote peer
// and sending/receiving packets across the network.
TransportChannelImpl* const channel_;
std::unique_ptr<TransportChannelImpl> channel_;
// Connectivity state of QuicTransportChannel.
QuicTransportState quic_state_ = QUIC_TRANSPORT_NEW;
// QUIC session which establishes the crypto handshake and converts data

View File

@ -94,15 +94,15 @@ class QuicTestPeer : public sigslot::has_slots<> {
explicit QuicTestPeer(const std::string& name)
: name_(name),
bytes_sent_(0),
ice_channel_(name_, 0),
quic_channel_(&ice_channel_),
ice_channel_(new FailableTransportChannel(name_, 0)),
quic_channel_(ice_channel_),
incoming_stream_count_(0) {
quic_channel_.SignalReadPacket.connect(
this, &QuicTestPeer::OnTransportChannelReadPacket);
quic_channel_.SignalIncomingStream.connect(this,
&QuicTestPeer::OnIncomingStream);
quic_channel_.SignalClosed.connect(this, &QuicTestPeer::OnClosed);
ice_channel_.SetAsync(true);
ice_channel_->SetAsync(true);
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate(name_, rtc::KT_DEFAULT)));
@ -112,13 +112,13 @@ class QuicTestPeer : public sigslot::has_slots<> {
// Connects |ice_channel_| to that of the other peer.
void Connect(QuicTestPeer* other_peer) {
ice_channel_.Connect();
other_peer->ice_channel_.Connect();
ice_channel_.SetDestination(&other_peer->ice_channel_);
ice_channel_->Connect();
other_peer->ice_channel_->Connect();
ice_channel_->SetDestination(other_peer->ice_channel_);
}
// Disconnects |ice_channel_|.
void Disconnect() { ice_channel_.SetDestination(nullptr); }
void Disconnect() { ice_channel_->SetDestination(nullptr); }
// Generates ICE credentials and passes them to |quic_channel_|.
void SetIceParameters(IceRole local_ice_role,
@ -189,13 +189,13 @@ class QuicTestPeer : public sigslot::has_slots<> {
void ClearBytesReceived() { bytes_received_ = 0; }
void SetWriteError(int error) { ice_channel_.SetError(error); }
void SetWriteError(int error) { ice_channel_->SetError(error); }
size_t bytes_received() const { return bytes_received_; }
size_t bytes_sent() const { return bytes_sent_; }
FailableTransportChannel* ice_channel() { return &ice_channel_; }
FailableTransportChannel* ice_channel() { return ice_channel_; }
QuicTransportChannel* quic_channel() { return &quic_channel_; }
@ -230,7 +230,7 @@ class QuicTestPeer : public sigslot::has_slots<> {
std::string name_; // Channel name.
size_t bytes_sent_; // Bytes sent by QUIC channel.
size_t bytes_received_; // Bytes received by QUIC channel.
FailableTransportChannel ice_channel_; // Simulates an ICE channel.
FailableTransportChannel* ice_channel_; // Simulates an ICE channel.
QuicTransportChannel quic_channel_; // QUIC channel to test.
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint_;
ReliableQuicStream* incoming_quic_stream_ = nullptr;