diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc | 380 |
1 files changed, 275 insertions, 105 deletions
diff --git a/chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc index 47c2b2ceebc..a7622f32d01 100644 --- a/chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc +++ b/chromium/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc @@ -4,9 +4,9 @@ #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h" #include "net/quic/quic_chromium_connection_helper.h" -#include "net/third_party/quic/core/crypto/proof_source.h" #include "net/third_party/quic/core/crypto/quic_random.h" #include "net/third_party/quic/core/quic_config.h" +#include "net/third_party/quic/core/quic_utils.h" #include "net/third_party/quic/core/tls_client_handshaker.h" #include "net/third_party/quic/core/tls_server_handshaker.h" #include "net/third_party/quic/tools/quic_simple_crypto_server_stream_helper.h" @@ -18,94 +18,159 @@ namespace { static const char kClosingDetails[] = "Application closed connection."; static const size_t kHostnameLength = 32; -// TODO(https://crbug.com/874300): Create a secure connection by implementing a -// P2PProofSource and P2PProofVerifier and remove these once the TLS 1.3 -// handshake is implemented for QUIC. This will allow us to verify for both the -// server and client: -// - The self signed certificate fingerprint matches the remote -// fingerprint that was signaled. -// - The peer owns the certificate, by verifying the signature of the hash of -// the handshake context. -// -// Used by QuicCryptoServerConfig to provide dummy proof credentials -// (taken from quic/quartc). -class DummyProofSource : public quic::ProofSource { +// QUIC's default is 100. Setting this value to 10000 allows room for QUIC to +// not refuse new incoming streams in the case that an application wants to send +// a small chunk of data per stream (and immediately close) unreliably. +uint32_t kMaxIncomingDynamicStreams = 10000; + +// The P2PQuicPacketWriter is a private helper class that implements the +// QuicPacketWriter using a P2PQuicPacketTransport. This allows us to +// connect our own packet transport for writing into the QuicConnection. +// The normal case is using an ICE transport (packet_transport) for writing. +class P2PQuicPacketWriter : public quic::QuicPacketWriter, + public P2PQuicPacketTransport::WriteObserver { public: - DummyProofSource() {} - ~DummyProofSource() override {} - - // ProofSource override. - void GetProof(const quic::QuicSocketAddress& server_addr, - const quic::QuicString& hostname, - const quic::QuicString& server_config, - quic::QuicTransportVersion transport_version, - quic::QuicStringPiece chlo_hash, - std::unique_ptr<Callback> callback) override { - quic::QuicReferenceCountedPointer<ProofSource::Chain> chain; - quic::QuicCryptoProof proof; - std::vector<quic::QuicString> certs; - certs.push_back("Dummy cert"); - chain = new ProofSource::Chain(certs); - proof.signature = "Dummy signature"; - proof.leaf_cert_scts = "Dummy timestamp"; - callback->Run(true, chain, proof, nullptr /* details */); + P2PQuicPacketWriter(P2PQuicPacketTransport* packet_transport) + : packet_transport_(packet_transport) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(packet_transport_); + packet_transport_->SetWriteObserver(this); } - quic::QuicReferenceCountedPointer<Chain> GetCertChain( - const quic::QuicSocketAddress& server_address, - const quic::QuicString& hostname) override { - return quic::QuicReferenceCountedPointer<Chain>(); + // This way the packet transport knows it no longer has a write observer and + // can DCHECK this on destruction. + ~P2PQuicPacketWriter() override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + packet_transport_->SetWriteObserver(nullptr); } - void ComputeTlsSignature( - const quic::QuicSocketAddress& server_address, - const quic::QuicString& hostname, - uint16_t signature_algorithm, - quic::QuicStringPiece in, - std::unique_ptr<SignatureCallback> callback) override { - callback->Run(true, "Dummy signature"); + // Sets the QuicConnection (which owns this packet writer). This allows us + // to get the packet numbers of QUIC packets we write. The QuicConnection + // is created with a quic::QuicPacketWriter, so we can't set the connection + // in the constructor. + void InitializeWithQuicConnection(quic::QuicConnection* connection) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(connection); + if (packet_transport_->Writable()) { + SetWritable(); + } + connection_ = connection; } -}; -// Used by QuicCryptoClientConfig to ignore the peer's credentials -// and establish an insecure QUIC connection (taken from quic/quartc). -class InsecureProofVerifier : public quic::ProofVerifier { - public: - InsecureProofVerifier() {} - ~InsecureProofVerifier() override {} - - // ProofVerifier override. - quic::QuicAsyncStatus VerifyProof( - const quic::QuicString& hostname, - const uint16_t port, - const quic::QuicString& server_config, - quic::QuicTransportVersion transport_version, - quic::QuicStringPiece chlo_hash, - const std::vector<quic::QuicString>& certs, - const quic::QuicString& cert_sct, - const quic::QuicString& signature, - const quic::ProofVerifyContext* context, - quic::QuicString* error_details, - std::unique_ptr<quic::ProofVerifyDetails>* verify_details, - std::unique_ptr<quic::ProofVerifierCallback> callback) override { - return quic::QUIC_SUCCESS; + // quic::QuicPacketWriter overrides. + + // Writes a QUIC packet to the network with the packet number as additional + // packet info. + quic::WriteResult WritePacket(const char* buffer, + size_t buf_len, + const quic::QuicIpAddress& self_address, + const quic::QuicSocketAddress& peer_address, + quic::PerPacketOptions* options) override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(connection_); + if (IsWriteBlocked()) { + return quic::WriteResult(quic::WRITE_STATUS_BLOCKED, EWOULDBLOCK); + } + + P2PQuicPacketTransport::QuicPacket packet; + packet.packet_number = connection_->packet_generator().packet_number(); + packet.buffer = buffer; + packet.buf_len = buf_len; + int bytes_written = packet_transport_->WritePacket(packet); + if (bytes_written <= 0) { + writable_ = false; + return quic::WriteResult(quic::WRITE_STATUS_BLOCKED, EWOULDBLOCK); + } + return quic::WriteResult(quic::WRITE_STATUS_OK, bytes_written); + } + + bool IsWriteBlockedDataBuffered() const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return false; + } + + bool IsWriteBlocked() const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return !writable_; } - quic::QuicAsyncStatus VerifyCertChain( - const quic::QuicString& hostname, - const std::vector<quic::QuicString>& certs, - const quic::ProofVerifyContext* context, - quic::QuicString* error_details, - std::unique_ptr<quic::ProofVerifyDetails>* details, - std::unique_ptr<quic::ProofVerifierCallback> callback) override { - return quic::QUIC_SUCCESS; + quic::QuicByteCount GetMaxPacketSize( + const quic::QuicSocketAddress& peer_address) const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + // This can be configured later. + return 1200; } - std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override { + void SetWritable() override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + writable_ = true; + } + + bool SupportsReleaseTime() const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return false; + } + + bool IsBatchMode() const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return false; + } + + char* GetNextWriteLocation( + const quic::QuicIpAddress& self_address, + const quic::QuicSocketAddress& peer_address) override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return nullptr; } + + quic::WriteResult Flush() override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return quic::WriteResult(quic::WRITE_STATUS_OK, 0); + } + + // P2PQuicPacketTransport::WriteDelegate override. + void OnCanWrite() override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + SetWritable(); + connection_->OnCanWrite(); + } + + private: + // The packet transport is owned by the P2PQuicSession, not the + // BlinkPacketWriter. + P2PQuicPacketTransport* packet_transport_; + // The QuicConnection owns this packet writer and will outlive it. + quic::QuicConnection* connection_; + + bool writable_ = false; + THREAD_CHECKER(thread_checker_); }; +// Creates the QuicConnection for the QuicSession. Currently this connection +// uses a dummy address and ID. The |packet_writer| is a basic implementation +// using the QuicTransportConfig::packet_transport for writing. The |helper| +// and |alarm_factory| should be chromium specific implementations. +std::unique_ptr<quic::QuicConnection> CreateQuicConnection( + quic::Perspective perspective, + quic::QuicConnectionHelperInterface* helper, + quic::QuicPacketWriter* packet_writer, + quic::QuicAlarmFactory* alarm_factory) { + quic::QuicIpAddress ip; + ip.FromString("0.0.0.0"); + quic::QuicSocketAddress dummy_address(ip, 0 /* Port */); + quic::QuicConnectionId dummy_connection_id; + if (GetQuicRestartFlag(quic_variable_length_connection_ids_client)) { + char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + dummy_connection_id = quic::QuicConnectionId(connection_id_bytes, + sizeof(connection_id_bytes)); + } else { + dummy_connection_id = quic::EmptyQuicConnectionId(); + } + return std::make_unique<quic::QuicConnection>( + dummy_connection_id, dummy_address, helper, alarm_factory, packet_writer, + /* owns_writer */ true, perspective, quic::CurrentSupportedVersions()); +} + // A dummy helper for a server crypto stream that accepts all client hellos // and generates a random connection ID. class DummyCryptoServerStreamHelper @@ -117,7 +182,7 @@ class DummyCryptoServerStreamHelper quic::QuicConnectionId GenerateConnectionIdForReject( quic::QuicConnectionId connection_id) const override { - return random_->RandUint64(); + return quic::QuicUtils::CreateRandomConnectionId(random_); } bool CanAcceptClientHello(const quic::CryptoHandshakeMessage& message, @@ -134,6 +199,56 @@ class DummyCryptoServerStreamHelper }; } // namespace +std::unique_ptr<P2PQuicTransportImpl> P2PQuicTransportImpl::Create( + quic::QuicClock* clock, + quic::QuicAlarmFactory* alarm_factory, + quic::QuicRandom* quic_random, + P2PQuicTransport::Delegate* delegate, + P2PQuicPacketTransport* packet_transport, + const P2PQuicTransportConfig& config, + std::unique_ptr<P2PQuicCryptoConfigFactory> crypto_config_factory) { + DCHECK(delegate); + DCHECK(packet_transport); + DCHECK(crypto_config_factory); + + // The P2PQuicSession owns these chromium specific objects required + // by the QuicConnection. These outlive the QuicConnection itself. + std::unique_ptr<net::QuicChromiumConnectionHelper> helper = + std::make_unique<net::QuicChromiumConnectionHelper>(clock, quic_random); + + P2PQuicPacketWriter* packet_writer = + new P2PQuicPacketWriter(packet_transport); + std::unique_ptr<quic::QuicConnection> quic_connection = CreateQuicConnection( + config.perspective, helper.get(), packet_writer, alarm_factory); + // It's okay for the quic::QuicConnection to have a P2PQuicPacketWriter before + // the P2PQuicPacketWriter is initialized, because the P2QuicPacketWriter + // won't be writable until this occurs. + packet_writer->InitializeWithQuicConnection(quic_connection.get()); + + // QUIC configurations for the session are specified here. + // TODO(shampson): Consider setting larger initial flow control window sizes + // so that the default limit doesn't cause initial undersending. + quic::QuicConfig quic_config; + quic_config.SetMaxIncomingDynamicStreamsToSend(kMaxIncomingDynamicStreams); + // The handshake network timeouts are configured to large values to prevent + // the QUIC connection from being closed on a slow connection. This can occur + // if signaling is slow and one side begins the handshake early. + // See ICE related bug: bugs.webrtc.org/9869. + // + // This timeout is from time of creation of the quic::QuicConnection object to + // the completion of the handshake. It must be larger than the idle time. + quic_config.set_max_time_before_crypto_handshake( + quic::QuicTime::Delta::FromSeconds(50)); + // This is the timeout for idle time in the handshake. This value allows + // time for slow signaling to complete. + quic_config.set_max_idle_time_before_crypto_handshake( + quic::QuicTime::Delta::FromSeconds(30)); + return std::make_unique<P2PQuicTransportImpl>( + delegate, packet_transport, std::move(config), std::move(helper), + std::move(quic_connection), quic_config, std::move(crypto_config_factory), + clock); +} + P2PQuicTransportImpl::P2PQuicTransportImpl( Delegate* delegate, P2PQuicPacketTransport* packet_transport, @@ -141,6 +256,7 @@ P2PQuicTransportImpl::P2PQuicTransportImpl( std::unique_ptr<net::QuicChromiumConnectionHelper> helper, std::unique_ptr<quic::QuicConnection> connection, const quic::QuicConfig& quic_config, + std::unique_ptr<P2PQuicCryptoConfigFactory> crypto_config_factory, quic::QuicClock* clock) : quic::QuicSession(connection.get(), nullptr /* visitor */, @@ -148,6 +264,7 @@ P2PQuicTransportImpl::P2PQuicTransportImpl( quic::CurrentSupportedVersions()), helper_(std::move(helper)), connection_(std::move(connection)), + crypto_config_factory_(std::move(crypto_config_factory)), perspective_(p2p_transport_config.perspective), packet_transport_(packet_transport), delegate_(delegate), @@ -157,18 +274,32 @@ P2PQuicTransportImpl::P2PQuicTransportImpl( stream_write_buffer_size_(p2p_transport_config.stream_write_buffer_size) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(delegate_); + DCHECK(crypto_config_factory_); DCHECK(clock_); DCHECK(packet_transport_); DCHECK_GT(stream_delegate_read_buffer_size_, 0u); DCHECK_GT(stream_write_buffer_size_, 0u); - DCHECK_GT(p2p_transport_config.certificates.size(), 0u); - if (p2p_transport_config.can_respond_to_crypto_handshake) { - InitializeCryptoStream(); + if (!p2p_transport_config.certificates.empty()) { + // TODO(https://crbug.com/874296): The web API accepts multiple + // certificates, and we might want to pass these down to let QUIC decide on + // what to use. + certificate_ = p2p_transport_config.certificates[0]; + } + switch (perspective_) { + case quic::Perspective::IS_CLIENT: { + crypto_client_config_ = + crypto_config_factory_->CreateClientCryptoConfig(); + break; + } + case quic::Perspective::IS_SERVER: { + crypto_server_config_ = + crypto_config_factory_->CreateServerCryptoConfig(); + break; + } + default: + NOTREACHED(); + break; } - // TODO(https://crbug.com/874296): The web API accepts multiple certificates, - // and we might want to pass these down to let QUIC decide on what to use. - certificate_ = p2p_transport_config.certificates[0]; - packet_transport_->SetReceiveDelegate(this); } P2PQuicTransportImpl::~P2PQuicTransportImpl() { @@ -176,6 +307,8 @@ P2PQuicTransportImpl::~P2PQuicTransportImpl() { } void P2PQuicTransportImpl::Stop() { + // This shouldn't be called before Start(). + DCHECK(crypto_stream_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (IsClosed()) { return; @@ -189,23 +322,38 @@ void P2PQuicTransportImpl::Stop() { quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } -void P2PQuicTransportImpl::Start( - std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints) { +void P2PQuicTransportImpl::Start(StartConfig config) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK_EQ(remote_fingerprints_.size(), 0u); - DCHECK_GT(remote_fingerprints.size(), 0u); - if (IsClosed()) { - // We could have received a close from the remote side before calling this. - return; + // Either the remote fingerprints are being verified or a pre shared key is + // set. + DCHECK((certificate_ && !config.remote_fingerprints.empty()) || + !config.pre_shared_key.empty()); + DCHECK(!crypto_stream_); + + remote_fingerprints_ = std::move(config.remote_fingerprints); + switch (perspective_) { + case quic::Perspective::IS_CLIENT: { + crypto_client_config_->set_pre_shared_key(config.pre_shared_key); + break; + } + case quic::Perspective::IS_SERVER: { + crypto_server_config_->set_pre_shared_key(config.pre_shared_key); + break; + } + default: + NOTREACHED(); + break; } - // These will be used to verify the remote certificate during the handshake. - remote_fingerprints_ = std::move(remote_fingerprints); + + InitializeCryptoStream(); if (perspective_ == quic::Perspective::IS_CLIENT) { quic::QuicCryptoClientStream* client_crypto_stream = static_cast<quic::QuicCryptoClientStream*>(crypto_stream_.get()); client_crypto_stream->CryptoConnect(); } + // Now that crypto streams are setup we are ready to receive QUIC packets. + packet_transport_->SetReceiveDelegate(this); } void P2PQuicTransportImpl::OnPacketDataReceived(const char* data, @@ -233,9 +381,17 @@ P2PQuicStreamImpl* P2PQuicTransportImpl::CreateStream() { return CreateOutgoingBidirectionalStream(); } +P2PQuicTransportStats P2PQuicTransportImpl::GetStats() const { + return P2PQuicTransportStats(connection_->GetStats(), + num_outgoing_streams_created_, + num_incoming_streams_created_); +} + P2PQuicStreamImpl* P2PQuicTransportImpl::CreateOutgoingBidirectionalStream() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - P2PQuicStreamImpl* stream = CreateStreamInternal(GetNextOutgoingStreamId()); + num_outgoing_streams_created_++; + P2PQuicStreamImpl* stream = + CreateStreamInternal(GetNextOutgoingBidirectionalStreamId()); ActivateStream(std::unique_ptr<P2PQuicStreamImpl>(stream)); return stream; } @@ -243,12 +399,23 @@ P2PQuicStreamImpl* P2PQuicTransportImpl::CreateOutgoingBidirectionalStream() { P2PQuicStreamImpl* P2PQuicTransportImpl::CreateIncomingStream( quic::QuicStreamId id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + num_incoming_streams_created_++; P2PQuicStreamImpl* stream = CreateStreamInternal(id); ActivateStream(std::unique_ptr<P2PQuicStreamImpl>(stream)); delegate_->OnStream(stream); return stream; } +P2PQuicStreamImpl* P2PQuicTransportImpl::CreateIncomingStream( + quic::PendingStream pending) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + num_incoming_streams_created_++; + P2PQuicStreamImpl* stream = CreateStreamInternal(std::move(pending)); + ActivateStream(std::unique_ptr<P2PQuicStreamImpl>(stream)); + delegate_->OnStream(stream); + return stream; +} + P2PQuicStreamImpl* P2PQuicTransportImpl::CreateStreamInternal( quic::QuicStreamId id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -259,19 +426,26 @@ P2PQuicStreamImpl* P2PQuicTransportImpl::CreateStreamInternal( stream_write_buffer_size_); } +P2PQuicStreamImpl* P2PQuicTransportImpl::CreateStreamInternal( + quic::PendingStream pending) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(crypto_stream_); + DCHECK(IsEncryptionEstablished()); + DCHECK(!IsClosed()); + return new P2PQuicStreamImpl(std::move(pending), this, + stream_delegate_read_buffer_size_, + stream_write_buffer_size_); +} + void P2PQuicTransportImpl::InitializeCryptoStream() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!crypto_stream_); + // TODO(shampson): If the P2PQuicTransportImpl is subclassed into a client + // and server class we can call this as a virtual function and not need this + // switch statement. switch (perspective_) { case quic::Perspective::IS_CLIENT: { - if (!crypto_client_config_) { - // The |crypto_client_config_| has not already been set (by the test). - std::unique_ptr<quic::ProofVerifier> proof_verifier( - new InsecureProofVerifier); - crypto_client_config_ = std::make_unique<quic::QuicCryptoClientConfig>( - std::move(proof_verifier), - quic::TlsClientHandshaker::CreateSslCtx()); - } + DCHECK(crypto_client_config_); // The host must be unique for every endpoint the client communicates // with. char random_hostname[kHostnameLength]; @@ -289,11 +463,7 @@ void P2PQuicTransportImpl::InitializeCryptoStream() { break; } case quic::Perspective::IS_SERVER: { - std::unique_ptr<quic::ProofSource> proof_source(new DummyProofSource); - crypto_server_config_ = std::make_unique<quic::QuicCryptoServerConfig>( - quic::QuicCryptoServerConfig::TESTING, helper_->GetRandomGenerator(), - std::move(proof_source), quic::KeyExchangeSource::Default(), - quic::TlsServerHandshaker::CreateSslCtx()); + DCHECK(crypto_server_config_); // Provide server with serialized config string to prove ownership. quic::QuicCryptoServerConfig::ConfigOptions options; // The |message| is used to handle the return value of AddDefaultConfig |