/* * Copyright 2010 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_TEST_FAKE_PORT_ALLOCATOR_H_ #define P2P_TEST_FAKE_PORT_ALLOCATOR_H_ #include #include #include #include #include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "api/candidate.h" #include "api/environment/environment.h" #include "api/packet_socket_factory.h" #include "api/task_queue/task_queue_base.h" #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" #include "p2p/base/stun_port.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" #include "rtc_base/net_test_helpers.h" #include "rtc_base/network.h" #include "rtc_base/socket_factory.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/weak_ptr.h" namespace webrtc { class TestUDPPort : public UDPPort { public: static TestUDPPort* Create(const PortParametersRef& args, uint16_t min_port, uint16_t max_port, bool emit_localhost_for_anyaddress) { TestUDPPort* port = new TestUDPPort(args, min_port, max_port, emit_localhost_for_anyaddress); if (!port->Init()) { delete port; port = nullptr; } return port; } static std::unique_ptr Create( const PortParametersRef& args, AsyncPacketSocket* socket, bool emit_localhost_for_anyaddress) { auto port = absl::WrapUnique( new TestUDPPort(args, socket, emit_localhost_for_anyaddress)); if (!port->Init()) { return nullptr; } return port; } protected: TestUDPPort(const PortParametersRef& args, uint16_t min_port, uint16_t max_port, bool emit_localhost_for_anyaddress) : UDPPort(args, IceCandidateType::kHost, min_port, max_port, emit_localhost_for_anyaddress) {} TestUDPPort(const PortParametersRef& args, AsyncPacketSocket* socket, bool emit_localhost_for_anyaddress) : UDPPort(args, IceCandidateType::kHost, socket, emit_localhost_for_anyaddress) {} }; // A FakePortAllocatorSession can be used with either a real or fake socket // factory. It gathers a single loopback port, using IPv6 if available and // not disabled. class FakePortAllocatorSession : public PortAllocatorSession { public: FakePortAllocatorSession(const Environment& env, PortAllocator* allocator, TaskQueueBase* network_thread, PacketSocketFactory* factory, absl::string_view content_name, int component, absl::string_view ice_ufrag, absl::string_view ice_pwd) : PortAllocatorSession(content_name, component, ice_ufrag, ice_pwd, allocator->flags()), env_(env), allocator_(allocator), network_thread_(network_thread), factory_(factory), ipv4_network_("network", "unittest", IPAddress(INADDR_LOOPBACK), 32), ipv6_network_("network", "unittest", IPAddress(in6addr_loopback), 64), port_(), port_config_count_(0), stun_servers_(allocator->stun_servers()), turn_servers_(allocator->turn_servers()) { ipv4_network_.AddIP(IPAddress(INADDR_LOOPBACK)); ipv6_network_.AddIP(IPAddress(in6addr_loopback)); } void SetCandidateFilter(uint32_t filter) override { candidate_filter_ = filter; } void StartGettingPorts() override { if (!port_) { Network& network = (HasIPv6Enabled() && (flags() & PORTALLOCATOR_ENABLE_IPV6)) ? ipv6_network_ : ipv4_network_; port_.reset(TestUDPPort::Create({.env = env_, .network_thread = network_thread_, .socket_factory = factory_, .network = &network, .ice_username_fragment = username(), .ice_password = password()}, 0, 0, false)); RTC_DCHECK(port_); port_->SetIceTiebreaker(allocator_->ice_tiebreaker()); port_->SubscribePortDestroyed( this, [this](PortInterface* port) { OnPortDestroyed(port); }); AddPort(port_.get()); } ++port_config_count_; running_ = true; } void StopGettingPorts() override { running_ = false; } bool IsGettingPorts() override { return running_; } void ClearGettingPorts() override { is_cleared = true; } bool IsCleared() const override { return is_cleared; } void RegatherOnFailedNetworks() override { NotifyIceRegathering(this, IceRegatheringReason::NETWORK_FAILURE); } std::vector ReadyPorts() const override { return ready_ports_; } std::vector ReadyCandidates() const override { return candidates_; } void PruneAllPorts() override { port_->Prune(); } bool CandidatesAllocationDone() const override { return allocation_done_; } int port_config_count() { return port_config_count_; } const ServerAddresses& stun_servers() const { return stun_servers_; } const std::vector& turn_servers() const { return turn_servers_; } uint32_t candidate_filter() const { return candidate_filter_; } int transport_info_update_count() const { return transport_info_update_count_; } protected: void UpdateIceParametersInternal() override { // Since this class is a fake and this method only is overridden for tests, // we don't need to actually update the transport info. ++transport_info_update_count_; } private: void AddPort(Port* port) { port->set_component(component()); port->set_generation(generation()); port->SubscribePortComplete(this, [this](Port* port) { OnPortComplete(port); }); port->PrepareAddress(); ready_ports_.push_back(port); NotifyPortReady(this, port); port->KeepAliveUntilPruned(); } void OnPortComplete(Port* port) { const std::vector& candidates = port->Candidates(); candidates_.insert(candidates_.end(), candidates.begin(), candidates.end()); NotifyCandidatesReady(this, candidates); allocation_done_ = true; NotifyCandidatesAllocationDone(this); } void OnPortDestroyed(PortInterface* /* port */) { // Don't want to double-delete port if it deletes itself. port_.release(); } const Environment env_; PortAllocator* allocator_; TaskQueueBase* network_thread_; PacketSocketFactory* factory_; Network ipv4_network_; Network ipv6_network_; std::unique_ptr port_; int port_config_count_; std::vector candidates_; std::vector ready_ports_; bool allocation_done_ = false; bool is_cleared = false; ServerAddresses stun_servers_; std::vector turn_servers_; uint32_t candidate_filter_ = CF_ALL; int transport_info_update_count_ = 0; bool running_ = false; }; class FakePortAllocator : public PortAllocator { public: FakePortAllocator( const Environment& env, SocketFactory* absl_nonnull socket_factory, TaskQueueBase* absl_nonnull network_thread = TaskQueueBase::Current()) : env_(env), network_thread_(network_thread), factory_(socket_factory) { RTC_CHECK(network_thread); SendTask(network_thread_, [this] { Initialize(); }); } void SetNetworkIgnoreMask(int /* network_ignore_mask */) override {} PortAllocatorSession* CreateSessionInternal( absl::string_view content_name, int component, absl::string_view ice_ufrag, absl::string_view ice_pwd) override { return new FakePortAllocatorSession(env_, this, network_thread_, &factory_, content_name, component, ice_ufrag, ice_pwd); } bool initialized() const { return initialized_; } // For testing: Manipulate MdnsObfuscationEnabled() bool MdnsObfuscationEnabled() const override { return mdns_obfuscation_enabled_; } void SetMdnsObfuscationEnabledForTesting(bool enabled) { mdns_obfuscation_enabled_ = enabled; } void DiscardCandidatePool() override { PortAllocator::DiscardCandidatePool(); if (on_candidate_pool_discarded_) { on_candidate_pool_discarded_(); } } void SetOnDiscardCandidatePool( absl::AnyInvocable on_candidate_pool_discarded) { on_candidate_pool_discarded_ = std::move(on_candidate_pool_discarded); } WeakPtr NewWeakPtr() { return weak_factory_.GetWeakPtr(); } private: const Environment env_; TaskQueueBase* absl_nonnull network_thread_; BasicPacketSocketFactory factory_; bool mdns_obfuscation_enabled_ = false; absl::AnyInvocable on_candidate_pool_discarded_; WeakPtrFactory weak_factory_{this}; }; } // namespace webrtc #endif // P2P_TEST_FAKE_PORT_ALLOCATOR_H_