From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- third_party/libwebrtc/p2p/base/dtls_transport.cc | 870 +++++++++++++++++++++++ 1 file changed, 870 insertions(+) create mode 100644 third_party/libwebrtc/p2p/base/dtls_transport.cc (limited to 'third_party/libwebrtc/p2p/base/dtls_transport.cc') diff --git a/third_party/libwebrtc/p2p/base/dtls_transport.cc b/third_party/libwebrtc/p2p/base/dtls_transport.cc new file mode 100644 index 0000000000..af16efad78 --- /dev/null +++ b/third_party/libwebrtc/p2p/base/dtls_transport.cc @@ -0,0 +1,870 @@ +/* + * Copyright 2011 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 "p2p/base/dtls_transport.h" + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/dtls_transport_interface.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/dscp.h" +#include "rtc_base/logging.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/thread.h" + +namespace cricket { + +// We don't pull the RTP constants from rtputils.h, to avoid a layer violation. +static const size_t kDtlsRecordHeaderLen = 13; +static const size_t kMaxDtlsPacketLen = 2048; +static const size_t kMinRtpPacketLen = 12; + +// Maximum number of pending packets in the queue. Packets are read immediately +// after they have been written, so a capacity of "1" is sufficient. +// +// However, this bug seems to indicate that's not the case: crbug.com/1063834 +// So, temporarily increasing it to 2 to see if that makes a difference. +static const size_t kMaxPendingPackets = 2; + +// Minimum and maximum values for the initial DTLS handshake timeout. We'll pick +// an initial timeout based on ICE RTT estimates, but clamp it to this range. +static const int kMinHandshakeTimeout = 50; +static const int kMaxHandshakeTimeout = 3000; + +static bool IsDtlsPacket(const char* data, size_t len) { + const uint8_t* u = reinterpret_cast(data); + return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); +} +static bool IsDtlsClientHelloPacket(const char* data, size_t len) { + if (!IsDtlsPacket(data, len)) { + return false; + } + const uint8_t* u = reinterpret_cast(data); + return len > 17 && u[0] == 22 && u[13] == 1; +} +static bool IsRtpPacket(const char* data, size_t len) { + const uint8_t* u = reinterpret_cast(data); + return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); +} + +StreamInterfaceChannel::StreamInterfaceChannel( + IceTransportInternal* ice_transport) + : ice_transport_(ice_transport), + state_(rtc::SS_OPEN), + packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {} + +rtc::StreamResult StreamInterfaceChannel::Read(rtc::ArrayView buffer, + size_t& read, + int& error) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + if (state_ == rtc::SS_CLOSED) + return rtc::SR_EOS; + if (state_ == rtc::SS_OPENING) + return rtc::SR_BLOCK; + + if (!packets_.ReadFront(buffer.data(), buffer.size(), &read)) { + return rtc::SR_BLOCK; + } + + return rtc::SR_SUCCESS; +} + +rtc::StreamResult StreamInterfaceChannel::Write( + rtc::ArrayView data, + size_t& written, + int& error) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // Always succeeds, since this is an unreliable transport anyway. + // TODO(zhihuang): Should this block if ice_transport_'s temporarily + // unwritable? + rtc::PacketOptions packet_options; + ice_transport_->SendPacket(reinterpret_cast(data.data()), + data.size(), packet_options); + written = data.size(); + return rtc::SR_SUCCESS; +} + +bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (packets_.size() > 0) { + RTC_LOG(LS_WARNING) << "Packet already in queue."; + } + bool ret = packets_.WriteBack(data, size, NULL); + if (!ret) { + // Somehow we received another packet before the SSLStreamAdapter read the + // previous one out of our temporary buffer. In this case, we'll log an + // error and still signal the read event, hoping that it will read the + // packet currently in packets_. + RTC_LOG(LS_ERROR) << "Failed to write packet to queue."; + } + SignalEvent(this, rtc::SE_READ, 0); + return ret; +} + +rtc::StreamState StreamInterfaceChannel::GetState() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return state_; +} + +void StreamInterfaceChannel::Close() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + packets_.Clear(); + state_ = rtc::SS_CLOSED; +} + +DtlsTransport::DtlsTransport(IceTransportInternal* ice_transport, + const webrtc::CryptoOptions& crypto_options, + webrtc::RtcEventLog* event_log, + rtc::SSLProtocolVersion max_version) + : component_(ice_transport->component()), + ice_transport_(ice_transport), + downward_(NULL), + srtp_ciphers_(crypto_options.GetSupportedDtlsSrtpCryptoSuites()), + ssl_max_version_(max_version), + event_log_(event_log) { + RTC_DCHECK(ice_transport_); + ConnectToIceTransport(); +} + +DtlsTransport::~DtlsTransport() = default; + +webrtc::DtlsTransportState DtlsTransport::dtls_state() const { + return dtls_state_; +} + +const std::string& DtlsTransport::transport_name() const { + return ice_transport_->transport_name(); +} + +int DtlsTransport::component() const { + return component_; +} + +bool DtlsTransport::IsDtlsActive() const { + return dtls_active_; +} + +bool DtlsTransport::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + if (dtls_active_) { + if (certificate == local_certificate_) { + // This may happen during renegotiation. + RTC_LOG(LS_INFO) << ToString() << ": Ignoring identical DTLS identity"; + return true; + } else { + RTC_LOG(LS_ERROR) << ToString() + << ": Can't change DTLS local identity in this state"; + return false; + } + } + + if (certificate) { + local_certificate_ = certificate; + dtls_active_ = true; + } else { + RTC_LOG(LS_INFO) << ToString() + << ": NULL DTLS identity supplied. Not doing DTLS"; + } + + return true; +} + +rtc::scoped_refptr DtlsTransport::GetLocalCertificate() + const { + return local_certificate_; +} + +bool DtlsTransport::SetDtlsRole(rtc::SSLRole role) { + if (dtls_) { + RTC_DCHECK(dtls_role_); + if (*dtls_role_ != role) { + RTC_LOG(LS_ERROR) + << "SSL Role can't be reversed after the session is setup."; + return false; + } + return true; + } + + dtls_role_ = role; + return true; +} + +bool DtlsTransport::GetDtlsRole(rtc::SSLRole* role) const { + if (!dtls_role_) { + return false; + } + *role = *dtls_role_; + return true; +} + +bool DtlsTransport::GetSslCipherSuite(int* cipher) { + if (dtls_state() != webrtc::DtlsTransportState::kConnected) { + return false; + } + + return dtls_->GetSslCipherSuite(cipher); +} + +webrtc::RTCError DtlsTransport::SetRemoteParameters( + absl::string_view digest_alg, + const uint8_t* digest, + size_t digest_len, + absl::optional role) { + rtc::Buffer remote_fingerprint_value(digest, digest_len); + bool is_dtls_restart = + dtls_active_ && remote_fingerprint_value_ != remote_fingerprint_value; + // Set SSL role. Role must be set before fingerprint is applied, which + // initiates DTLS setup. + if (role) { + if (is_dtls_restart) { + dtls_role_ = *role; + } else { + if (!SetDtlsRole(*role)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to set SSL role for the transport."); + } + } + } + // Apply remote fingerprint. + if (!SetRemoteFingerprint(digest_alg, digest, digest_len)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to apply remote fingerprint."); + } + return webrtc::RTCError::OK(); +} + +bool DtlsTransport::SetRemoteFingerprint(absl::string_view digest_alg, + const uint8_t* digest, + size_t digest_len) { + rtc::Buffer remote_fingerprint_value(digest, digest_len); + + // Once we have the local certificate, the same remote fingerprint can be set + // multiple times. + if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && + !digest_alg.empty()) { + // This may happen during renegotiation. + RTC_LOG(LS_INFO) << ToString() + << ": Ignoring identical remote DTLS fingerprint"; + return true; + } + + // If the other side doesn't support DTLS, turn off `dtls_active_`. + // TODO(deadbeef): Remove this. It's dangerous, because it relies on higher + // level code to ensure DTLS is actually used, but there are tests that + // depend on it, for the case where an m= section is rejected. In that case + // SetRemoteFingerprint shouldn't even be called though. + if (digest_alg.empty()) { + RTC_DCHECK(!digest_len); + RTC_LOG(LS_INFO) << ToString() << ": Other side didn't support DTLS."; + dtls_active_ = false; + return true; + } + + // Otherwise, we must have a local certificate before setting remote + // fingerprint. + if (!dtls_active_) { + RTC_LOG(LS_ERROR) << ToString() + << ": Can't set DTLS remote settings in this state."; + return false; + } + + // At this point we know we are doing DTLS + bool fingerprint_changing = remote_fingerprint_value_.size() > 0u; + remote_fingerprint_value_ = std::move(remote_fingerprint_value); + remote_fingerprint_algorithm_ = std::string(digest_alg); + + if (dtls_ && !fingerprint_changing) { + // This can occur if DTLS is set up before a remote fingerprint is + // received. For instance, if we set up DTLS due to receiving an early + // ClientHello. + rtc::SSLPeerCertificateDigestError err; + if (!dtls_->SetPeerCertificateDigest( + remote_fingerprint_algorithm_, + reinterpret_cast(remote_fingerprint_value_.data()), + remote_fingerprint_value_.size(), &err)) { + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't set DTLS certificate digest."; + set_dtls_state(webrtc::DtlsTransportState::kFailed); + // If the error is "verification failed", don't return false, because + // this means the fingerprint was formatted correctly but didn't match + // the certificate from the DTLS handshake. Thus the DTLS state should go + // to "failed", but SetRemoteDescription shouldn't fail. + return err == rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED; + } + return true; + } + + // If the fingerprint is changing, we'll tear down the DTLS association and + // create a new one, resetting our state. + if (dtls_ && fingerprint_changing) { + dtls_.reset(nullptr); + set_dtls_state(webrtc::DtlsTransportState::kNew); + set_writable(false); + } + + if (!SetupDtls()) { + set_dtls_state(webrtc::DtlsTransportState::kFailed); + return false; + } + + return true; +} + +std::unique_ptr DtlsTransport::GetRemoteSSLCertChain() + const { + if (!dtls_) { + return nullptr; + } + + return dtls_->GetPeerSSLCertChain(); +} + +bool DtlsTransport::ExportKeyingMaterial(absl::string_view label, + const uint8_t* context, + size_t context_len, + bool use_context, + uint8_t* result, + size_t result_len) { + return (dtls_.get()) + ? dtls_->ExportKeyingMaterial(label, context, context_len, + use_context, result, result_len) + : false; +} + +bool DtlsTransport::SetupDtls() { + RTC_DCHECK(dtls_role_); + { + auto downward = std::make_unique(ice_transport_); + StreamInterfaceChannel* downward_ptr = downward.get(); + + dtls_ = rtc::SSLStreamAdapter::Create(std::move(downward)); + if (!dtls_) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to create DTLS adapter."; + return false; + } + downward_ = downward_ptr; + } + + dtls_->SetIdentity(local_certificate_->identity()->Clone()); + dtls_->SetMode(rtc::SSL_MODE_DTLS); + dtls_->SetMaxProtocolVersion(ssl_max_version_); + dtls_->SetServerRole(*dtls_role_); + dtls_->SignalEvent.connect(this, &DtlsTransport::OnDtlsEvent); + dtls_->SignalSSLHandshakeError.connect(this, + &DtlsTransport::OnDtlsHandshakeError); + if (remote_fingerprint_value_.size() && + !dtls_->SetPeerCertificateDigest( + remote_fingerprint_algorithm_, + reinterpret_cast(remote_fingerprint_value_.data()), + remote_fingerprint_value_.size())) { + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't set DTLS certificate digest."; + return false; + } + + // Set up DTLS-SRTP, if it's been enabled. + if (!srtp_ciphers_.empty()) { + if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) { + RTC_LOG(LS_ERROR) << ToString() << ": Couldn't set DTLS-SRTP ciphers."; + return false; + } + } else { + RTC_LOG(LS_INFO) << ToString() << ": Not using DTLS-SRTP."; + } + + RTC_LOG(LS_INFO) << ToString() << ": DTLS setup complete."; + + // If the underlying ice_transport is already writable at this point, we may + // be able to start DTLS right away. + MaybeStartDtls(); + return true; +} + +bool DtlsTransport::GetSrtpCryptoSuite(int* cipher) { + if (dtls_state() != webrtc::DtlsTransportState::kConnected) { + return false; + } + + return dtls_->GetDtlsSrtpCryptoSuite(cipher); +} + +bool DtlsTransport::GetSslVersionBytes(int* version) const { + if (dtls_state() != webrtc::DtlsTransportState::kConnected) { + return false; + } + + return dtls_->GetSslVersionBytes(version); +} + +// Called from upper layers to send a media packet. +int DtlsTransport::SendPacket(const char* data, + size_t size, + const rtc::PacketOptions& options, + int flags) { + if (!dtls_active_) { + // Not doing DTLS. + return ice_transport_->SendPacket(data, size, options); + } + + switch (dtls_state()) { + case webrtc::DtlsTransportState::kNew: + // Can't send data until the connection is active. + // TODO(ekr@rtfm.com): assert here if dtls_ is NULL? + return -1; + case webrtc::DtlsTransportState::kConnecting: + // Can't send data until the connection is active. + return -1; + case webrtc::DtlsTransportState::kConnected: + if (flags & PF_SRTP_BYPASS) { + RTC_DCHECK(!srtp_ciphers_.empty()); + if (!IsRtpPacket(data, size)) { + return -1; + } + + return ice_transport_->SendPacket(data, size, options); + } else { + size_t written; + int error; + return (dtls_->WriteAll( + rtc::MakeArrayView(reinterpret_cast(data), + size), + written, error) == rtc::SR_SUCCESS) + ? static_cast(size) + : -1; + } + case webrtc::DtlsTransportState::kFailed: + // Can't send anything when we're failed. + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't send packet due to " + "webrtc::DtlsTransportState::kFailed."; + return -1; + case webrtc::DtlsTransportState::kClosed: + // Can't send anything when we're closed. + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't send packet due to " + "webrtc::DtlsTransportState::kClosed."; + return -1; + default: + RTC_DCHECK_NOTREACHED(); + return -1; + } +} + +IceTransportInternal* DtlsTransport::ice_transport() { + return ice_transport_; +} + +bool DtlsTransport::IsDtlsConnected() { + return dtls_ && dtls_->IsTlsConnected(); +} + +bool DtlsTransport::receiving() const { + return receiving_; +} + +bool DtlsTransport::writable() const { + return writable_; +} + +int DtlsTransport::GetError() { + return ice_transport_->GetError(); +} + +absl::optional DtlsTransport::network_route() const { + return ice_transport_->network_route(); +} + +bool DtlsTransport::GetOption(rtc::Socket::Option opt, int* value) { + return ice_transport_->GetOption(opt, value); +} + +int DtlsTransport::SetOption(rtc::Socket::Option opt, int value) { + return ice_transport_->SetOption(opt, value); +} + +void DtlsTransport::ConnectToIceTransport() { + RTC_DCHECK(ice_transport_); + ice_transport_->SignalWritableState.connect(this, + &DtlsTransport::OnWritableState); + ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket); + ice_transport_->SignalSentPacket.connect(this, &DtlsTransport::OnSentPacket); + ice_transport_->SignalReadyToSend.connect(this, + &DtlsTransport::OnReadyToSend); + ice_transport_->SignalReceivingState.connect( + this, &DtlsTransport::OnReceivingState); + ice_transport_->SignalNetworkRouteChanged.connect( + this, &DtlsTransport::OnNetworkRouteChanged); +} + +// The state transition logic here is as follows: +// (1) If we're not doing DTLS-SRTP, then the state is just the +// state of the underlying impl() +// (2) If we're doing DTLS-SRTP: +// - Prior to the DTLS handshake, the state is neither receiving nor +// writable +// - When the impl goes writable for the first time we +// start the DTLS handshake +// - Once the DTLS handshake completes, the state is that of the +// impl again +void DtlsTransport::OnWritableState(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_LOG(LS_VERBOSE) << ToString() + << ": ice_transport writable state changed to " + << ice_transport_->writable(); + + if (!dtls_active_) { + // Not doing DTLS. + // Note: SignalWritableState fired by set_writable. + set_writable(ice_transport_->writable()); + return; + } + + switch (dtls_state()) { + case webrtc::DtlsTransportState::kNew: + MaybeStartDtls(); + break; + case webrtc::DtlsTransportState::kConnected: + // Note: SignalWritableState fired by set_writable. + set_writable(ice_transport_->writable()); + break; + case webrtc::DtlsTransportState::kConnecting: + // Do nothing. + break; + case webrtc::DtlsTransportState::kFailed: + // Should not happen. Do nothing. + RTC_LOG(LS_ERROR) << ToString() + << ": OnWritableState() called in state " + "webrtc::DtlsTransportState::kFailed."; + break; + case webrtc::DtlsTransportState::kClosed: + // Should not happen. Do nothing. + RTC_LOG(LS_ERROR) << ToString() + << ": OnWritableState() called in state " + "webrtc::DtlsTransportState::kClosed."; + break; + case webrtc::DtlsTransportState::kNumValues: + RTC_DCHECK_NOTREACHED(); + break; + } +} + +void DtlsTransport::OnReceivingState(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_LOG(LS_VERBOSE) << ToString() + << ": ice_transport " + "receiving state changed to " + << ice_transport_->receiving(); + if (!dtls_active_ || dtls_state() == webrtc::DtlsTransportState::kConnected) { + // Note: SignalReceivingState fired by set_receiving. + set_receiving(ice_transport_->receiving()); + } +} + +void DtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t size, + const int64_t& packet_time_us, + int flags) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_DCHECK(flags == 0); + + if (!dtls_active_) { + // Not doing DTLS. + SignalReadPacket(this, data, size, packet_time_us, 0); + return; + } + + switch (dtls_state()) { + case webrtc::DtlsTransportState::kNew: + if (dtls_) { + RTC_LOG(LS_INFO) << ToString() + << ": Packet received before DTLS started."; + } else { + RTC_LOG(LS_WARNING) << ToString() + << ": Packet received before we know if we are " + "doing DTLS or not."; + } + // Cache a client hello packet received before DTLS has actually started. + if (IsDtlsClientHelloPacket(data, size)) { + RTC_LOG(LS_INFO) << ToString() + << ": Caching DTLS ClientHello packet until DTLS is " + "started."; + cached_client_hello_.SetData(data, size); + // If we haven't started setting up DTLS yet (because we don't have a + // remote fingerprint/role), we can use the client hello as a clue that + // the peer has chosen the client role, and proceed with the handshake. + // The fingerprint will be verified when it's set. + if (!dtls_ && local_certificate_) { + SetDtlsRole(rtc::SSL_SERVER); + SetupDtls(); + } + } else { + RTC_LOG(LS_INFO) << ToString() + << ": Not a DTLS ClientHello packet; dropping."; + } + break; + + case webrtc::DtlsTransportState::kConnecting: + case webrtc::DtlsTransportState::kConnected: + // We should only get DTLS or SRTP packets; STUN's already been demuxed. + // Is this potentially a DTLS packet? + if (IsDtlsPacket(data, size)) { + if (!HandleDtlsPacket(data, size)) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; + return; + } + } else { + // Not a DTLS packet; our handshake should be complete by now. + if (dtls_state() != webrtc::DtlsTransportState::kConnected) { + RTC_LOG(LS_ERROR) << ToString() + << ": Received non-DTLS packet before DTLS " + "complete."; + return; + } + + // And it had better be a SRTP packet. + if (!IsRtpPacket(data, size)) { + RTC_LOG(LS_ERROR) + << ToString() << ": Received unexpected non-DTLS packet."; + return; + } + + // Sanity check. + RTC_DCHECK(!srtp_ciphers_.empty()); + + // Signal this upwards as a bypass packet. + SignalReadPacket(this, data, size, packet_time_us, PF_SRTP_BYPASS); + } + break; + case webrtc::DtlsTransportState::kFailed: + case webrtc::DtlsTransportState::kClosed: + case webrtc::DtlsTransportState::kNumValues: + // This shouldn't be happening. Drop the packet. + break; + } +} + +void DtlsTransport::OnSentPacket(rtc::PacketTransportInternal* transport, + const rtc::SentPacket& sent_packet) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalSentPacket(this, sent_packet); +} + +void DtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + if (writable()) { + SignalReadyToSend(this); + } +} + +void DtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(dtls == dtls_.get()); + if (sig & rtc::SE_OPEN) { + // This is the first time. + RTC_LOG(LS_INFO) << ToString() << ": DTLS handshake complete."; + if (dtls_->GetState() == rtc::SS_OPEN) { + // The check for OPEN shouldn't be necessary but let's make + // sure we don't accidentally frob the state if it's closed. + set_dtls_state(webrtc::DtlsTransportState::kConnected); + set_writable(true); + } + } + if (sig & rtc::SE_READ) { + uint8_t buf[kMaxDtlsPacketLen]; + size_t read; + int read_error; + rtc::StreamResult ret; + // The underlying DTLS stream may have received multiple DTLS records in + // one packet, so read all of them. + do { + ret = dtls_->Read(buf, read, read_error); + if (ret == rtc::SR_SUCCESS) { + SignalReadPacket(this, reinterpret_cast(buf), read, + rtc::TimeMicros(), 0); + } else if (ret == rtc::SR_EOS) { + // Remote peer shut down the association with no error. + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote"; + set_writable(false); + set_dtls_state(webrtc::DtlsTransportState::kClosed); + SignalClosed(this); + } else if (ret == rtc::SR_ERROR) { + // Remote peer shut down the association with an error. + RTC_LOG(LS_INFO) + << ToString() + << ": Closed by remote with DTLS transport error, code=" + << read_error; + set_writable(false); + set_dtls_state(webrtc::DtlsTransportState::kFailed); + SignalClosed(this); + } + } while (ret == rtc::SR_SUCCESS); + } + if (sig & rtc::SE_CLOSE) { + RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself. + set_writable(false); + if (!err) { + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed"; + set_dtls_state(webrtc::DtlsTransportState::kClosed); + } else { + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport error, code=" << err; + set_dtls_state(webrtc::DtlsTransportState::kFailed); + } + } +} + +void DtlsTransport::OnNetworkRouteChanged( + absl::optional network_route) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalNetworkRouteChanged(network_route); +} + +void DtlsTransport::MaybeStartDtls() { + if (dtls_ && ice_transport_->writable()) { + ConfigureHandshakeTimeout(); + + if (dtls_->StartSSL()) { + // This should never fail: + // Because we are operating in a nonblocking mode and all + // incoming packets come in via OnReadPacket(), which rejects + // packets in this state, the incoming queue must be empty. We + // ignore write errors, thus any errors must be because of + // configuration and therefore are our fault. + RTC_DCHECK_NOTREACHED() << "StartSSL failed."; + RTC_LOG(LS_ERROR) << ToString() << ": Couldn't start DTLS handshake"; + set_dtls_state(webrtc::DtlsTransportState::kFailed); + return; + } + RTC_LOG(LS_INFO) << ToString() + << ": DtlsTransport: Started DTLS handshake active=" + << IsDtlsActive(); + set_dtls_state(webrtc::DtlsTransportState::kConnecting); + // Now that the handshake has started, we can process a cached ClientHello + // (if one exists). + if (cached_client_hello_.size()) { + if (*dtls_role_ == rtc::SSL_SERVER) { + RTC_LOG(LS_INFO) << ToString() + << ": Handling cached DTLS ClientHello packet."; + if (!HandleDtlsPacket(cached_client_hello_.data(), + cached_client_hello_.size())) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; + } + } else { + RTC_LOG(LS_WARNING) << ToString() + << ": Discarding cached DTLS ClientHello packet " + "because we don't have the server role."; + } + cached_client_hello_.Clear(); + } + } +} + +// Called from OnReadPacket when a DTLS packet is received. +bool DtlsTransport::HandleDtlsPacket(const char* data, size_t size) { + // Sanity check we're not passing junk that + // just looks like DTLS. + const uint8_t* tmp_data = reinterpret_cast(data); + size_t tmp_size = size; + while (tmp_size > 0) { + if (tmp_size < kDtlsRecordHeaderLen) + return false; // Too short for the header + + size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]); + if ((record_len + kDtlsRecordHeaderLen) > tmp_size) + return false; // Body too short + + tmp_data += record_len + kDtlsRecordHeaderLen; + tmp_size -= record_len + kDtlsRecordHeaderLen; + } + + // Looks good. Pass to the SIC which ends up being passed to + // the DTLS stack. + return downward_->OnPacketReceived(data, size); +} + +void DtlsTransport::set_receiving(bool receiving) { + if (receiving_ == receiving) { + return; + } + receiving_ = receiving; + SignalReceivingState(this); +} + +void DtlsTransport::set_writable(bool writable) { + if (writable_ == writable) { + return; + } + if (event_log_) { + event_log_->Log( + std::make_unique(writable)); + } + RTC_LOG(LS_VERBOSE) << ToString() << ": set_writable to: " << writable; + writable_ = writable; + if (writable_) { + SignalReadyToSend(this); + } + SignalWritableState(this); +} + +void DtlsTransport::set_dtls_state(webrtc::DtlsTransportState state) { + if (dtls_state_ == state) { + return; + } + if (event_log_) { + event_log_->Log( + std::make_unique(state)); + } + RTC_LOG(LS_VERBOSE) << ToString() << ": set_dtls_state from:" + << static_cast(dtls_state_) << " to " + << static_cast(state); + dtls_state_ = state; + SendDtlsState(this, state); +} + +void DtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError error) { + SendDtlsHandshakeError(error); +} + +void DtlsTransport::ConfigureHandshakeTimeout() { + RTC_DCHECK(dtls_); + absl::optional rtt = ice_transport_->GetRttEstimate(); + if (rtt) { + // Limit the timeout to a reasonable range in case the ICE RTT takes + // extreme values. + int initial_timeout = std::max(kMinHandshakeTimeout, + std::min(kMaxHandshakeTimeout, 2 * (*rtt))); + RTC_LOG(LS_INFO) << ToString() << ": configuring DTLS handshake timeout " + << initial_timeout << " based on ICE RTT " << *rtt; + + dtls_->SetInitialRetransmissionTimeout(initial_timeout); + } else { + RTC_LOG(LS_INFO) + << ToString() + << ": no RTT estimate - using default DTLS handshake timeout"; + } +} + +} // namespace cricket -- cgit v1.2.3