blob: 48f1efb1991e6569e15d7093b46625661f022aee [file] [log] [blame]
/*
* 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_BASE_FAKE_PORT_ALLOCATOR_H_
#define P2P_BASE_FAKE_PORT_ALLOCATOR_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/base/port_allocator.h"
#include "p2p/base/udp_port.h"
#include "rtc_base/memory/always_valid_pointer.h"
#include "rtc_base/net_helpers.h"
#include "rtc_base/task_queue_for_test.h"
#include "rtc_base/thread.h"
#include "test/scoped_key_value_config.h"
namespace rtc {
class SocketFactory;
}
namespace cricket {
class TestUDPPort : public UDPPort {
public:
static TestUDPPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
const rtc::Network* network,
uint16_t min_port,
uint16_t max_port,
absl::string_view username,
absl::string_view password,
bool emit_localhost_for_anyaddress,
const webrtc::FieldTrialsView* field_trials) {
TestUDPPort* port =
new TestUDPPort(thread, factory, network, min_port, max_port, username,
password, emit_localhost_for_anyaddress, field_trials);
if (!port->Init()) {
delete port;
port = nullptr;
}
return port;
}
protected:
TestUDPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
const rtc::Network* network,
uint16_t min_port,
uint16_t max_port,
absl::string_view username,
absl::string_view password,
bool emit_localhost_for_anyaddress,
const webrtc::FieldTrialsView* field_trials)
: UDPPort(thread,
factory,
network,
min_port,
max_port,
username,
password,
emit_localhost_for_anyaddress,
field_trials) {}
};
// 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(PortAllocator* allocator,
rtc::Thread* network_thread,
rtc::PacketSocketFactory* factory,
absl::string_view content_name,
int component,
absl::string_view ice_ufrag,
absl::string_view ice_pwd,
const webrtc::FieldTrialsView& field_trials)
: PortAllocatorSession(content_name,
component,
ice_ufrag,
ice_pwd,
allocator->flags()),
network_thread_(network_thread),
factory_(factory),
ipv4_network_("network",
"unittest",
rtc::IPAddress(INADDR_LOOPBACK),
32),
ipv6_network_("network",
"unittest",
rtc::IPAddress(in6addr_loopback),
64),
port_(),
port_config_count_(0),
stun_servers_(allocator->stun_servers()),
turn_servers_(allocator->turn_servers()),
field_trials_(field_trials) {
ipv4_network_.AddIP(rtc::IPAddress(INADDR_LOOPBACK));
ipv6_network_.AddIP(rtc::IPAddress(in6addr_loopback));
set_ice_tiebreaker(/*kTiebreakerDefault = */ 44444);
}
void SetCandidateFilter(uint32_t filter) override {
candidate_filter_ = filter;
}
void StartGettingPorts() override {
if (!port_) {
rtc::Network& network =
(rtc::HasIPv6Enabled() && (flags() & PORTALLOCATOR_ENABLE_IPV6))
? ipv6_network_
: ipv4_network_;
port_.reset(TestUDPPort::Create(network_thread_, factory_, &network, 0, 0,
username(), password(), false,
&field_trials_));
RTC_DCHECK(port_);
port_->SetIceTiebreaker(ice_tiebreaker());
port_->SubscribePortDestroyed(
[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 {
SignalIceRegathering(this, IceRegatheringReason::NETWORK_FAILURE);
}
std::vector<PortInterface*> ReadyPorts() const override {
return ready_ports_;
}
std::vector<Candidate> 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<RelayServerConfig>& 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(cricket::Port* port) {
port->set_component(component());
port->set_generation(generation());
port->SignalPortComplete.connect(this,
&FakePortAllocatorSession::OnPortComplete);
port->PrepareAddress();
ready_ports_.push_back(port);
SignalPortReady(this, port);
port->KeepAliveUntilPruned();
}
void OnPortComplete(cricket::Port* port) {
const std::vector<Candidate>& candidates = port->Candidates();
candidates_.insert(candidates_.end(), candidates.begin(), candidates.end());
SignalCandidatesReady(this, candidates);
allocation_done_ = true;
SignalCandidatesAllocationDone(this);
}
void OnPortDestroyed(cricket::PortInterface* port) {
// Don't want to double-delete port if it deletes itself.
port_.release();
}
rtc::Thread* network_thread_;
rtc::PacketSocketFactory* factory_;
rtc::Network ipv4_network_;
rtc::Network ipv6_network_;
std::unique_ptr<cricket::Port> port_;
int port_config_count_;
std::vector<Candidate> candidates_;
std::vector<PortInterface*> ready_ports_;
bool allocation_done_ = false;
bool is_cleared = false;
ServerAddresses stun_servers_;
std::vector<RelayServerConfig> turn_servers_;
uint32_t candidate_filter_ = CF_ALL;
int transport_info_update_count_ = 0;
bool running_ = false;
const webrtc::FieldTrialsView& field_trials_;
};
class FakePortAllocator : public cricket::PortAllocator {
public:
FakePortAllocator(rtc::Thread* network_thread,
rtc::PacketSocketFactory* factory)
: FakePortAllocator(network_thread, factory, nullptr) {}
FakePortAllocator(rtc::Thread* network_thread,
std::unique_ptr<rtc::PacketSocketFactory> factory)
: FakePortAllocator(network_thread, nullptr, std::move(factory)) {}
void SetNetworkIgnoreMask(int network_ignore_mask) override {}
cricket::PortAllocatorSession* CreateSessionInternal(
absl::string_view content_name,
int component,
absl::string_view ice_ufrag,
absl::string_view ice_pwd) override {
return new FakePortAllocatorSession(
this, network_thread_, factory_.get(), std::string(content_name),
component, std::string(ice_ufrag), std::string(ice_pwd), field_trials_);
}
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;
}
private:
FakePortAllocator(rtc::Thread* network_thread,
rtc::PacketSocketFactory* factory,
std::unique_ptr<rtc::PacketSocketFactory> owned_factory)
: network_thread_(network_thread),
factory_(std::move(owned_factory), factory) {
if (network_thread_ == nullptr) {
network_thread_ = rtc::Thread::Current();
Initialize();
return;
}
SendTask(network_thread_, [this] { Initialize(); });
}
webrtc::test::ScopedKeyValueConfig field_trials_;
rtc::Thread* network_thread_;
const webrtc::AlwaysValidPointerNoDefault<rtc::PacketSocketFactory> factory_;
bool mdns_obfuscation_enabled_ = false;
};
} // namespace cricket
#endif // P2P_BASE_FAKE_PORT_ALLOCATOR_H_