| /* |
| * Copyright 2017 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 P2P_BASE_FAKE_DTLS_TRANSPORT_H_ |
| #define P2P_BASE_FAKE_DTLS_TRANSPORT_H_ |
| |
| #include <cstring> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/strings/string_view.h" |
| #include "api/crypto/crypto_options.h" |
| #include "api/dtls_transport_interface.h" |
| #include "p2p/base/dtls_transport_internal.h" |
| #include "p2p/base/fake_ice_transport.h" |
| #include "rtc_base/fake_ssl_identity.h" |
| #include "rtc_base/network/received_packet.h" |
| #include "rtc_base/rtc_certificate.h" |
| |
| namespace cricket { |
| |
| // Fake DTLS transport which is implemented by wrapping a fake ICE transport. |
| // Doesn't interact directly with fake ICE transport for anything other than |
| // sending packets. |
| class FakeDtlsTransport : public DtlsTransportInternal { |
| public: |
| explicit FakeDtlsTransport(FakeIceTransport* ice_transport) |
| : ice_transport_(ice_transport), |
| transport_name_(ice_transport->transport_name()), |
| component_(ice_transport->component()), |
| dtls_fingerprint_("", nullptr) { |
| RTC_DCHECK(ice_transport_); |
| ice_transport_->RegisterReceivedPacketCallback( |
| this, [&](rtc::PacketTransportInternal* transport, |
| const rtc::ReceivedPacket& packet) { |
| OnIceTransportReadPacket(transport, packet); |
| }); |
| ice_transport_->SignalNetworkRouteChanged.connect( |
| this, &FakeDtlsTransport::OnNetworkRouteChanged); |
| } |
| |
| explicit FakeDtlsTransport(std::unique_ptr<FakeIceTransport> ice) |
| : owned_ice_transport_(std::move(ice)), |
| transport_name_(owned_ice_transport_->transport_name()), |
| component_(owned_ice_transport_->component()), |
| dtls_fingerprint_("", rtc::ArrayView<const uint8_t>()) { |
| ice_transport_ = owned_ice_transport_.get(); |
| ice_transport_->RegisterReceivedPacketCallback( |
| this, [&](rtc::PacketTransportInternal* transport, |
| const rtc::ReceivedPacket& packet) { |
| OnIceTransportReadPacket(transport, packet); |
| }); |
| ice_transport_->SignalNetworkRouteChanged.connect( |
| this, &FakeDtlsTransport::OnNetworkRouteChanged); |
| } |
| |
| // If this constructor is called, a new fake ICE transport will be created, |
| // and this FakeDtlsTransport will take the ownership. |
| FakeDtlsTransport(const std::string& name, int component) |
| : FakeDtlsTransport(std::make_unique<FakeIceTransport>(name, component)) { |
| } |
| FakeDtlsTransport(const std::string& name, |
| int component, |
| rtc::Thread* network_thread) |
| : FakeDtlsTransport(std::make_unique<FakeIceTransport>(name, |
| component, |
| network_thread)) {} |
| |
| ~FakeDtlsTransport() override { |
| if (dest_ && dest_->dest_ == this) { |
| dest_->dest_ = nullptr; |
| } |
| ice_transport_->DeregisterReceivedPacketCallback(this); |
| } |
| |
| // Get inner fake ICE transport. |
| FakeIceTransport* fake_ice_transport() { return ice_transport_; } |
| |
| // If async, will send packets by "Post"-ing to message queue instead of |
| // synchronously "Send"-ing. |
| void SetAsync(bool async) { ice_transport_->SetAsync(async); } |
| void SetAsyncDelay(int delay_ms) { ice_transport_->SetAsyncDelay(delay_ms); } |
| |
| // SetWritable, SetReceiving and SetDestination are the main methods that can |
| // be used for testing, to simulate connectivity or lack thereof. |
| void SetWritable(bool writable) { |
| ice_transport_->SetWritable(writable); |
| set_writable(writable); |
| } |
| void SetReceiving(bool receiving) { |
| ice_transport_->SetReceiving(receiving); |
| set_receiving(receiving); |
| } |
| void SetDtlsState(webrtc::DtlsTransportState state) { |
| dtls_state_ = state; |
| SendDtlsState(this, dtls_state_); |
| } |
| |
| // Simulates the two DTLS transports connecting to each other. |
| // If `asymmetric` is true this method only affects this FakeDtlsTransport. |
| // If false, it affects `dest` as well. |
| void SetDestination(FakeDtlsTransport* dest, bool asymmetric = false) { |
| if (dest == dest_) { |
| return; |
| } |
| RTC_DCHECK(!dest || !dest_) |
| << "Changing fake destination from one to another is not supported."; |
| if (dest && !dest_) { |
| // This simulates the DTLS handshake. |
| dest_ = dest; |
| if (local_cert_ && dest_->local_cert_) { |
| do_dtls_ = true; |
| RTC_LOG(LS_INFO) << "FakeDtlsTransport is doing DTLS"; |
| } else { |
| do_dtls_ = false; |
| RTC_LOG(LS_INFO) << "FakeDtlsTransport is not doing DTLS"; |
| } |
| SetWritable(true); |
| if (!asymmetric) { |
| dest->SetDestination(this, true); |
| } |
| // If the `dtls_role_` is unset, set it to SSL_CLIENT by default. |
| if (!dtls_role_) { |
| dtls_role_ = std::move(rtc::SSL_CLIENT); |
| } |
| SetDtlsState(webrtc::DtlsTransportState::kConnected); |
| ice_transport_->SetDestination( |
| static_cast<FakeIceTransport*>(dest->ice_transport()), asymmetric); |
| } else { |
| // Simulates loss of connectivity, by asymmetrically forgetting dest_. |
| dest_ = nullptr; |
| SetWritable(false); |
| ice_transport_->SetDestination(nullptr, asymmetric); |
| } |
| } |
| |
| // Fake DtlsTransportInternal implementation. |
| webrtc::DtlsTransportState dtls_state() const override { return dtls_state_; } |
| const std::string& transport_name() const override { return transport_name_; } |
| int component() const override { return component_; } |
| const rtc::SSLFingerprint& dtls_fingerprint() const { |
| return dtls_fingerprint_; |
| } |
| webrtc::RTCError SetRemoteParameters(absl::string_view alg, |
| const uint8_t* digest, |
| size_t digest_len, |
| std::optional<rtc::SSLRole> role) { |
| if (role) { |
| SetDtlsRole(*role); |
| } |
| SetRemoteFingerprint(alg, digest, digest_len); |
| return webrtc::RTCError::OK(); |
| } |
| bool SetRemoteFingerprint(absl::string_view alg, |
| const uint8_t* digest, |
| size_t digest_len) { |
| dtls_fingerprint_ = |
| rtc::SSLFingerprint(alg, rtc::MakeArrayView(digest, digest_len)); |
| return true; |
| } |
| bool SetDtlsRole(rtc::SSLRole role) override { |
| dtls_role_ = std::move(role); |
| return true; |
| } |
| bool GetDtlsRole(rtc::SSLRole* role) const override { |
| if (!dtls_role_) { |
| return false; |
| } |
| *role = *dtls_role_; |
| return true; |
| } |
| bool SetLocalCertificate( |
| const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override { |
| do_dtls_ = true; |
| local_cert_ = certificate; |
| return true; |
| } |
| void SetRemoteSSLCertificate(rtc::FakeSSLCertificate* cert) { |
| remote_cert_ = cert; |
| } |
| bool IsDtlsActive() const override { return do_dtls_; } |
| bool GetSslVersionBytes(int* version) const override { |
| if (!do_dtls_) { |
| return false; |
| } |
| *version = 0x0102; |
| return true; |
| } |
| bool GetSrtpCryptoSuite(int* crypto_suite) const override { |
| if (!do_dtls_) { |
| return false; |
| } |
| *crypto_suite = crypto_suite_; |
| return true; |
| } |
| void SetSrtpCryptoSuite(int crypto_suite) { crypto_suite_ = crypto_suite; } |
| |
| bool GetSslCipherSuite(int* cipher_suite) const override { |
| if (ssl_cipher_suite_) { |
| *cipher_suite = *ssl_cipher_suite_; |
| return true; |
| } |
| return false; |
| } |
| void SetSslCipherSuite(std::optional<int> cipher_suite) { |
| ssl_cipher_suite_ = cipher_suite; |
| } |
| |
| std::optional<absl::string_view> GetTlsCipherSuiteName() const override { |
| return "FakeTlsCipherSuite"; |
| } |
| uint16_t GetSslPeerSignatureAlgorithm() const override { return 0; } |
| rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override { |
| return local_cert_; |
| } |
| std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const override { |
| if (!remote_cert_) { |
| return nullptr; |
| } |
| return std::make_unique<rtc::SSLCertChain>(remote_cert_->Clone()); |
| } |
| bool ExportSrtpKeyingMaterial( |
| rtc::ZeroOnFreeBuffer<uint8_t>& keying_material) override { |
| if (do_dtls_) { |
| std::memset(keying_material.data(), 0xff, keying_material.size()); |
| } |
| return do_dtls_; |
| } |
| void set_ssl_max_protocol_version(rtc::SSLProtocolVersion version) { |
| ssl_max_version_ = version; |
| } |
| rtc::SSLProtocolVersion ssl_max_protocol_version() const { |
| return ssl_max_version_; |
| } |
| |
| IceTransportInternal* ice_transport() override { return ice_transport_; } |
| |
| // PacketTransportInternal implementation, which passes through to fake ICE |
| // transport for sending actual packets. |
| bool writable() const override { return writable_; } |
| bool receiving() const override { return receiving_; } |
| int SendPacket(const char* data, |
| size_t len, |
| const rtc::PacketOptions& options, |
| int flags) override { |
| // We expect only SRTP packets to be sent through this interface. |
| if (flags != PF_SRTP_BYPASS && flags != 0) { |
| return -1; |
| } |
| return ice_transport_->SendPacket(data, len, options, flags); |
| } |
| int SetOption(rtc::Socket::Option opt, int value) override { |
| return ice_transport_->SetOption(opt, value); |
| } |
| bool GetOption(rtc::Socket::Option opt, int* value) override { |
| return ice_transport_->GetOption(opt, value); |
| } |
| int GetError() override { return ice_transport_->GetError(); } |
| |
| std::optional<rtc::NetworkRoute> network_route() const override { |
| return ice_transport_->network_route(); |
| } |
| |
| private: |
| void OnIceTransportReadPacket(PacketTransportInternal* /* ice_ */, |
| const rtc::ReceivedPacket& packet) { |
| NotifyPacketReceived(packet); |
| } |
| |
| void set_receiving(bool receiving) { |
| if (receiving_ == receiving) { |
| return; |
| } |
| receiving_ = receiving; |
| SignalReceivingState(this); |
| } |
| |
| void set_writable(bool writable) { |
| if (writable_ == writable) { |
| return; |
| } |
| writable_ = writable; |
| if (writable_) { |
| SignalReadyToSend(this); |
| } |
| SignalWritableState(this); |
| } |
| |
| void OnNetworkRouteChanged(std::optional<rtc::NetworkRoute> network_route) { |
| SignalNetworkRouteChanged(network_route); |
| } |
| |
| FakeIceTransport* ice_transport_; |
| std::unique_ptr<FakeIceTransport> owned_ice_transport_; |
| std::string transport_name_; |
| int component_; |
| FakeDtlsTransport* dest_ = nullptr; |
| rtc::scoped_refptr<rtc::RTCCertificate> local_cert_; |
| rtc::FakeSSLCertificate* remote_cert_ = nullptr; |
| bool do_dtls_ = false; |
| rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12; |
| rtc::SSLFingerprint dtls_fingerprint_; |
| std::optional<rtc::SSLRole> dtls_role_; |
| int crypto_suite_ = rtc::kSrtpAes128CmSha1_80; |
| std::optional<int> ssl_cipher_suite_; |
| |
| webrtc::DtlsTransportState dtls_state_ = webrtc::DtlsTransportState::kNew; |
| |
| bool receiving_ = false; |
| bool writable_ = false; |
| }; |
| |
| } // namespace cricket |
| |
| #endif // P2P_BASE_FAKE_DTLS_TRANSPORT_H_ |