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}
diff --git a/webrtc/base/sslfingerprint.cc b/webrtc/base/sslfingerprint.cc
index 1939b4f..2c3e1e9 100644
--- a/webrtc/base/sslfingerprint.cc
+++ b/webrtc/base/sslfingerprint.cc
@@ -85,7 +85,7 @@
return fingerprint;
}
-std::string SSLFingerprint::ToString() {
+std::string SSLFingerprint::ToString() const {
std::string fp_str = algorithm;
fp_str.append(" ");
fp_str.append(GetRfc4572Fingerprint());
diff --git a/webrtc/base/sslfingerprint.h b/webrtc/base/sslfingerprint.h
index 1413a4c..4ffb2b0 100644
--- a/webrtc/base/sslfingerprint.h
+++ b/webrtc/base/sslfingerprint.h
@@ -41,7 +41,7 @@
std::string GetRfc4572Fingerprint() const;
- std::string ToString();
+ std::string ToString() const;
std::string algorithm;
rtc::CopyOnWriteBuffer digest;
diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h
index 2ff2ea5..a4bf383 100644
--- a/webrtc/p2p/base/dtlstransport.h
+++ b/webrtc/p2p/base/dtlstransport.h
@@ -66,27 +66,11 @@
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 @@
"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);
}
diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h
index e2bdc10..25e5e81 100644
--- a/webrtc/p2p/base/faketransportcontroller.h
+++ b/webrtc/p2p/base/faketransportcontroller.h
@@ -406,6 +406,8 @@
using Transport::local_description;
using Transport::remote_description;
+ using Transport::VerifyCertificateFingerprint;
+ using Transport::NegotiateRole;
protected:
TransportChannelImpl* CreateTransportChannel(int component) override {
diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc
index 89f05e8..3e45d20 100644
--- a/webrtc/p2p/base/transport.cc
+++ b/webrtc/p2p/base/transport.cc
@@ -401,4 +401,107 @@
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
diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h
index 46360cc..e31d37a 100644
--- a/webrtc/p2p/base/transport.h
+++ b/webrtc/p2p/base/transport.h
@@ -315,6 +315,20 @@
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.
diff --git a/webrtc/p2p/base/transport_unittest.cc b/webrtc/p2p/base/transport_unittest.cc
index 658c143..fba72b4 100644
--- a/webrtc/p2p/base/transport_unittest.cc
+++ b/webrtc/p2p/base/transport_unittest.cc
@@ -235,3 +235,180 @@
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));
+ }
+}
diff --git a/webrtc/p2p/base/transportcontroller.cc b/webrtc/p2p/base/transportcontroller.cc
index 24f6481..2eb198e 100644
--- a/webrtc/p2p/base/transportcontroller.cc
+++ b/webrtc/p2p/base/transportcontroller.cc
@@ -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 @@
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;
diff --git a/webrtc/p2p/base/transportcontroller.h b/webrtc/p2p/base/transportcontroller.h
index 8cb5986..f84c551 100644
--- a/webrtc/p2p/base/transportcontroller.h
+++ b/webrtc/p2p/base/transportcontroller.h
@@ -91,6 +91,9 @@
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 @@
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
diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp
index 5c9a575..50941c8 100644
--- a/webrtc/p2p/p2p.gyp
+++ b/webrtc/p2p/p2p.gyp
@@ -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',
],
diff --git a/webrtc/p2p/quic/quictransport.cc b/webrtc/p2p/quic/quictransport.cc
new file mode 100644
index 0000000..51f9a2b
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport.cc
@@ -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
diff --git a/webrtc/p2p/quic/quictransport.h b/webrtc/p2p/quic/quictransport.h
new file mode 100644
index 0000000..053ba61
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport.h
@@ -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_
diff --git a/webrtc/p2p/quic/quictransport_unittest.cc b/webrtc/p2p/quic/quictransport_unittest.cc
new file mode 100644
index 0000000..bc99f6b
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport_unittest.cc
@@ -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));
+}
diff --git a/webrtc/p2p/quic/quictransportchannel.cc b/webrtc/p2p/quic/quictransportchannel.cc
index b3f9180..968faee 100644
--- a/webrtc/p2p/quic/quictransportchannel.cc
+++ b/webrtc/p2p/quic/quictransportchannel.cc
@@ -274,7 +274,7 @@
// |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::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 @@
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::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 @@
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);
}
diff --git a/webrtc/p2p/quic/quictransportchannel.h b/webrtc/p2p/quic/quictransportchannel.h
index 847af7f..2ce17f8 100644
--- a/webrtc/p2p/quic/quictransportchannel.h
+++ b/webrtc/p2p/quic/quictransportchannel.h
@@ -273,7 +273,7 @@
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
diff --git a/webrtc/p2p/quic/quictransportchannel_unittest.cc b/webrtc/p2p/quic/quictransportchannel_unittest.cc
index dba07eb..0e16390 100644
--- a/webrtc/p2p/quic/quictransportchannel_unittest.cc
+++ b/webrtc/p2p/quic/quictransportchannel_unittest.cc
@@ -94,15 +94,15 @@
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 @@
// 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 @@
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 @@
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;