summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/p2p/base/stun_port_unittest.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/p2p/base/stun_port_unittest.cc')
-rw-r--r--third_party/libwebrtc/p2p/base/stun_port_unittest.cc744
1 files changed, 744 insertions, 0 deletions
diff --git a/third_party/libwebrtc/p2p/base/stun_port_unittest.cc b/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
new file mode 100644
index 0000000000..bf51151536
--- /dev/null
+++ b/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2009 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/stun_port.h"
+
+#include <memory>
+
+#include "api/test/mock_async_dns_resolver.h"
+#include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/base/mock_dns_resolving_packet_socket_factory.h"
+#include "p2p/base/test_stun_server.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/socket_address.h"
+#include "rtc_base/ssl_adapter.h"
+#include "rtc_base/virtual_socket_server.h"
+#include "test/gmock.h"
+#include "test/scoped_key_value_config.h"
+
+namespace {
+
+using cricket::ServerAddresses;
+using rtc::SocketAddress;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::ReturnPointee;
+using ::testing::SetArgPointee;
+
+static const SocketAddress kLocalAddr("127.0.0.1", 0);
+static const SocketAddress kIPv6LocalAddr("::1", 0);
+static const SocketAddress kStunAddr1("127.0.0.1", 5000);
+static const SocketAddress kStunAddr2("127.0.0.1", 4000);
+static const SocketAddress kStunAddr3("127.0.0.1", 3000);
+static const SocketAddress kIPv6StunAddr1("::1", 5000);
+static const SocketAddress kBadAddr("0.0.0.1", 5000);
+static const SocketAddress kValidHostnameAddr("valid-hostname", 5000);
+static const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000);
+// STUN timeout (with all retries) is cricket::STUN_TOTAL_TIMEOUT.
+// Add some margin of error for slow bots.
+static const int kTimeoutMs = cricket::STUN_TOTAL_TIMEOUT;
+// stun prio = 100 (srflx) << 24 | 30 (IPv4) << 8 | 256 - 1 (component)
+static const uint32_t kStunCandidatePriority =
+ (100 << 24) | (30 << 8) | (256 - 1);
+// stun prio = 100 (srflx) << 24 | 60 (loopback IPv6) << 8 | 256 - 1 (component)
+static const uint32_t kIPv6StunCandidatePriority =
+ (100 << 24) | (60 << 8) | (256 - 1);
+static const int kInfiniteLifetime = -1;
+static const int kHighCostPortKeepaliveLifetimeMs = 2 * 60 * 1000;
+
+constexpr uint64_t kTiebreakerDefault = 44444;
+
+class FakeMdnsResponder : public webrtc::MdnsResponderInterface {
+ public:
+ void CreateNameForAddress(const rtc::IPAddress& addr,
+ NameCreatedCallback callback) override {
+ callback(addr, std::string("unittest-mdns-host-name.local"));
+ }
+
+ void RemoveNameForAddress(const rtc::IPAddress& addr,
+ NameRemovedCallback callback) override {}
+};
+
+class FakeMdnsResponderProvider : public rtc::MdnsResponderProvider {
+ public:
+ FakeMdnsResponderProvider() : mdns_responder_(new FakeMdnsResponder()) {}
+
+ webrtc::MdnsResponderInterface* GetMdnsResponder() const override {
+ return mdns_responder_.get();
+ }
+
+ private:
+ std::unique_ptr<webrtc::MdnsResponderInterface> mdns_responder_;
+};
+
+// Base class for tests connecting a StunPort to a fake STUN server
+// (cricket::StunServer).
+class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> {
+ public:
+ StunPortTestBase()
+ : StunPortTestBase(
+ rtc::Network("unittest", "unittest", kLocalAddr.ipaddr(), 32),
+ kLocalAddr.ipaddr()) {}
+
+ StunPortTestBase(rtc::Network network, const rtc::IPAddress address)
+ : ss_(new rtc::VirtualSocketServer()),
+ thread_(ss_.get()),
+ network_(network),
+ socket_factory_(ss_.get()),
+ stun_server_1_(cricket::TestStunServer::Create(ss_.get(), kStunAddr1)),
+ stun_server_2_(cricket::TestStunServer::Create(ss_.get(), kStunAddr2)),
+ mdns_responder_provider_(new FakeMdnsResponderProvider()),
+ done_(false),
+ error_(false),
+ stun_keepalive_delay_(1),
+ stun_keepalive_lifetime_(-1) {
+ network_.AddIP(address);
+ }
+
+ virtual rtc::PacketSocketFactory* socket_factory() {
+ return &socket_factory_;
+ }
+
+ rtc::VirtualSocketServer* ss() const { return ss_.get(); }
+ cricket::UDPPort* port() const { return stun_port_.get(); }
+ rtc::AsyncPacketSocket* socket() const { return socket_.get(); }
+ bool done() const { return done_; }
+ bool error() const { return error_; }
+
+ bool HasPendingRequest(int msg_type) {
+ return stun_port_->request_manager().HasRequestForTest(msg_type);
+ }
+
+ void SetNetworkType(rtc::AdapterType adapter_type) {
+ network_.set_type(adapter_type);
+ }
+
+ void CreateStunPort(const rtc::SocketAddress& server_addr,
+ const webrtc::FieldTrialsView* field_trials = nullptr) {
+ ServerAddresses stun_servers;
+ stun_servers.insert(server_addr);
+ CreateStunPort(stun_servers, field_trials);
+ }
+
+ void CreateStunPort(const ServerAddresses& stun_servers,
+ const webrtc::FieldTrialsView* field_trials = nullptr) {
+ stun_port_ = cricket::StunPort::Create(
+ rtc::Thread::Current(), socket_factory(), &network_, 0, 0,
+ rtc::CreateRandomString(16), rtc::CreateRandomString(22), stun_servers,
+ absl::nullopt, field_trials);
+ stun_port_->SetIceTiebreaker(kTiebreakerDefault);
+ stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_);
+ // If `stun_keepalive_lifetime_` is negative, let the stun port
+ // choose its lifetime from the network type.
+ if (stun_keepalive_lifetime_ >= 0) {
+ stun_port_->set_stun_keepalive_lifetime(stun_keepalive_lifetime_);
+ }
+ stun_port_->SignalPortComplete.connect(this,
+ &StunPortTestBase::OnPortComplete);
+ stun_port_->SignalPortError.connect(this, &StunPortTestBase::OnPortError);
+ stun_port_->SignalCandidateError.connect(
+ this, &StunPortTestBase::OnCandidateError);
+ }
+
+ void CreateSharedUdpPort(
+ const rtc::SocketAddress& server_addr,
+ rtc::AsyncPacketSocket* socket,
+ const webrtc::FieldTrialsView* field_trials = nullptr) {
+ if (socket) {
+ socket_.reset(socket);
+ } else {
+ socket_.reset(socket_factory()->CreateUdpSocket(
+ rtc::SocketAddress(kLocalAddr.ipaddr(), 0), 0, 0));
+ }
+ ASSERT_TRUE(socket_ != NULL);
+ socket_->SignalReadPacket.connect(this, &StunPortTestBase::OnReadPacket);
+ stun_port_ = cricket::UDPPort::Create(
+ rtc::Thread::Current(), socket_factory(), &network_, socket_.get(),
+ rtc::CreateRandomString(16), rtc::CreateRandomString(22), false,
+ absl::nullopt, field_trials);
+ ASSERT_TRUE(stun_port_ != NULL);
+ stun_port_->SetIceTiebreaker(kTiebreakerDefault);
+ ServerAddresses stun_servers;
+ stun_servers.insert(server_addr);
+ stun_port_->set_server_addresses(stun_servers);
+ stun_port_->SignalPortComplete.connect(this,
+ &StunPortTestBase::OnPortComplete);
+ stun_port_->SignalPortError.connect(this, &StunPortTestBase::OnPortError);
+ }
+
+ void PrepareAddress() { stun_port_->PrepareAddress(); }
+
+ void OnReadPacket(rtc::AsyncPacketSocket* socket,
+ const char* data,
+ size_t size,
+ const rtc::SocketAddress& remote_addr,
+ const int64_t& /* packet_time_us */) {
+ stun_port_->HandleIncomingPacket(socket, data, size, remote_addr,
+ /* packet_time_us */ -1);
+ }
+
+ void SendData(const char* data, size_t len) {
+ stun_port_->HandleIncomingPacket(socket_.get(), data, len,
+ rtc::SocketAddress("22.22.22.22", 0),
+ /* packet_time_us */ -1);
+ }
+
+ void EnableMdnsObfuscation() {
+ network_.set_mdns_responder_provider(mdns_responder_provider_.get());
+ }
+
+ protected:
+ static void SetUpTestSuite() {
+ // Ensure the RNG is inited.
+ rtc::InitRandom(NULL, 0);
+ }
+
+ void OnPortComplete(cricket::Port* port) {
+ ASSERT_FALSE(done_);
+ done_ = true;
+ error_ = false;
+ }
+ void OnPortError(cricket::Port* port) {
+ done_ = true;
+ error_ = true;
+ }
+ void OnCandidateError(cricket::Port* port,
+ const cricket::IceCandidateErrorEvent& event) {
+ error_event_ = event;
+ }
+ void SetKeepaliveDelay(int delay) { stun_keepalive_delay_ = delay; }
+
+ void SetKeepaliveLifetime(int lifetime) {
+ stun_keepalive_lifetime_ = lifetime;
+ }
+
+ cricket::TestStunServer* stun_server_1() { return stun_server_1_.get(); }
+ cricket::TestStunServer* stun_server_2() { return stun_server_2_.get(); }
+
+ private:
+ std::unique_ptr<rtc::VirtualSocketServer> ss_;
+ rtc::AutoSocketServerThread thread_;
+ rtc::Network network_;
+ rtc::BasicPacketSocketFactory socket_factory_;
+ std::unique_ptr<cricket::UDPPort> stun_port_;
+ std::unique_ptr<cricket::TestStunServer> stun_server_1_;
+ std::unique_ptr<cricket::TestStunServer> stun_server_2_;
+ std::unique_ptr<rtc::AsyncPacketSocket> socket_;
+ std::unique_ptr<rtc::MdnsResponderProvider> mdns_responder_provider_;
+ bool done_;
+ bool error_;
+ int stun_keepalive_delay_;
+ int stun_keepalive_lifetime_;
+
+ protected:
+ cricket::IceCandidateErrorEvent error_event_;
+};
+
+class StunPortTestWithRealClock : public StunPortTestBase {};
+
+class FakeClockBase {
+ public:
+ rtc::ScopedFakeClock fake_clock;
+};
+
+class StunPortTest : public FakeClockBase, public StunPortTestBase {};
+
+// Test that we can create a STUN port.
+TEST_F(StunPortTest, TestCreateStunPort) {
+ CreateStunPort(kStunAddr1);
+ EXPECT_EQ("stun", port()->Type());
+ EXPECT_EQ(0U, port()->Candidates().size());
+}
+
+// Test that we can create a UDP port.
+TEST_F(StunPortTest, TestCreateUdpPort) {
+ CreateSharedUdpPort(kStunAddr1, nullptr);
+ EXPECT_EQ("local", port()->Type());
+ EXPECT_EQ(0U, port()->Candidates().size());
+}
+
+// Test that we can get an address from a STUN server.
+TEST_F(StunPortTest, TestPrepareAddress) {
+ CreateStunPort(kStunAddr1);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ std::string expected_server_url = "stun:127.0.0.1:5000";
+ EXPECT_EQ(port()->Candidates()[0].url(), expected_server_url);
+}
+
+// Test that we fail properly if we can't get an address.
+TEST_F(StunPortTest, TestPrepareAddressFail) {
+ CreateStunPort(kBadAddr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_TRUE(error());
+ EXPECT_EQ(0U, port()->Candidates().size());
+ EXPECT_EQ_SIMULATED_WAIT(error_event_.error_code,
+ cricket::SERVER_NOT_REACHABLE_ERROR, kTimeoutMs,
+ fake_clock);
+ ASSERT_NE(error_event_.error_text.find('.'), std::string::npos);
+ ASSERT_NE(error_event_.address.find(kLocalAddr.HostAsSensitiveURIString()),
+ std::string::npos);
+ std::string server_url = "stun:" + kBadAddr.ToString();
+ ASSERT_EQ(error_event_.url, server_url);
+}
+
+class StunPortWithMockDnsResolverTest : public StunPortTest {
+ public:
+ StunPortWithMockDnsResolverTest() : StunPortTest(), socket_factory_(ss()) {}
+
+ rtc::PacketSocketFactory* socket_factory() override {
+ return &socket_factory_;
+ }
+
+ void SetDnsResolverExpectations(
+ rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
+ socket_factory_.SetExpectations(expectations);
+ }
+
+ private:
+ rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
+};
+
+// Test that we can get an address from a STUN server specified by a hostname.
+TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostname) {
+ SetDnsResolverExpectations(
+ [](webrtc::MockAsyncDnsResolver* resolver,
+ webrtc::MockAsyncDnsResolverResult* resolver_result) {
+ EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _))
+ .WillOnce([](const rtc::SocketAddress& addr, int family,
+ absl::AnyInvocable<void()> callback) { callback(); });
+
+ EXPECT_CALL(*resolver, result)
+ .WillRepeatedly(ReturnPointee(resolver_result));
+ EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0));
+ EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _))
+ .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("127.0.0.1", 5000)),
+ Return(true)));
+ });
+ CreateStunPort(kValidHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority());
+}
+
+TEST_F(StunPortWithMockDnsResolverTest,
+ TestPrepareAddressHostnameWithPriorityAdjustment) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/");
+ SetDnsResolverExpectations(
+ [](webrtc::MockAsyncDnsResolver* resolver,
+ webrtc::MockAsyncDnsResolverResult* resolver_result) {
+ EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _))
+ .WillOnce([](const rtc::SocketAddress& addr, int family,
+ absl::AnyInvocable<void()> callback) { callback(); });
+ EXPECT_CALL(*resolver, result)
+ .WillRepeatedly(ReturnPointee(resolver_result));
+ EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0));
+ EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _))
+ .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("127.0.0.1", 5000)),
+ Return(true)));
+ });
+ CreateStunPort(kValidHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_EQ(kStunCandidatePriority + (cricket::kMaxTurnServers << 8),
+ port()->Candidates()[0].priority());
+}
+
+// Test that we handle hostname lookup failures properly.
+TEST_F(StunPortTestWithRealClock, TestPrepareAddressHostnameFail) {
+ CreateStunPort(kBadHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ EXPECT_TRUE(error());
+ EXPECT_EQ(0U, port()->Candidates().size());
+ EXPECT_EQ_WAIT(error_event_.error_code, cricket::SERVER_NOT_REACHABLE_ERROR,
+ kTimeoutMs);
+}
+
+// This test verifies keepalive response messages don't result in
+// additional candidate generation.
+TEST_F(StunPortTest, TestKeepAliveResponse) {
+ SetKeepaliveDelay(500); // 500ms of keepalive delay.
+ CreateStunPort(kStunAddr1);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ SIMULATED_WAIT(false, 1000, fake_clock);
+ EXPECT_EQ(1U, port()->Candidates().size());
+}
+
+// Test that a local candidate can be generated using a shared socket.
+TEST_F(StunPortTest, TestSharedSocketPrepareAddress) {
+ CreateSharedUdpPort(kStunAddr1, nullptr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+}
+
+// Test that we still get a local candidate with invalid stun server hostname.
+// Also verifing that UDPPort can receive packets when stun address can't be
+// resolved.
+TEST_F(StunPortTestWithRealClock,
+ TestSharedSocketPrepareAddressInvalidHostname) {
+ CreateSharedUdpPort(kBadHostnameAddr, nullptr);
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+
+ // Send data to port after it's ready. This is to make sure, UDP port can
+ // handle data with unresolved stun server address.
+ std::string data = "some random data, sending to cricket::Port.";
+ SendData(data.c_str(), data.length());
+ // No crash is success.
+}
+
+// Test that a stun candidate (srflx candidate) is discarded whose address is
+// equal to that of a local candidate if mDNS obfuscation is not enabled.
+TEST_F(StunPortTest, TestStunCandidateDiscardedWithMdnsObfuscationNotEnabled) {
+ CreateSharedUdpPort(kStunAddr1, nullptr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_EQ(port()->Candidates()[0].type(), cricket::LOCAL_PORT_TYPE);
+}
+
+// Test that a stun candidate (srflx candidate) is generated whose address is
+// equal to that of a local candidate if mDNS obfuscation is enabled.
+TEST_F(StunPortTest, TestStunCandidateGeneratedWithMdnsObfuscationEnabled) {
+ EnableMdnsObfuscation();
+ CreateSharedUdpPort(kStunAddr1, nullptr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(2U, port()->Candidates().size());
+
+ // The addresses of the candidates are both equal to kLocalAddr.
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[1].address()));
+
+ // One of the generated candidates is a local candidate and the other is a
+ // stun candidate.
+ EXPECT_NE(port()->Candidates()[0].type(), port()->Candidates()[1].type());
+ if (port()->Candidates()[0].type() == cricket::LOCAL_PORT_TYPE) {
+ EXPECT_EQ(port()->Candidates()[1].type(), cricket::STUN_PORT_TYPE);
+ } else {
+ EXPECT_EQ(port()->Candidates()[0].type(), cricket::STUN_PORT_TYPE);
+ EXPECT_EQ(port()->Candidates()[1].type(), cricket::LOCAL_PORT_TYPE);
+ }
+}
+
+// Test that the same address is added only once if two STUN servers are in
+// use.
+TEST_F(StunPortTest, TestNoDuplicatedAddressWithTwoStunServers) {
+ ServerAddresses stun_servers;
+ stun_servers.insert(kStunAddr1);
+ stun_servers.insert(kStunAddr2);
+ CreateStunPort(stun_servers);
+ EXPECT_EQ("stun", port()->Type());
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_EQ(1U, port()->Candidates().size());
+ EXPECT_EQ(port()->Candidates()[0].relay_protocol(), "");
+}
+
+// Test that candidates can be allocated for multiple STUN servers, one of
+// which is not reachable.
+TEST_F(StunPortTest, TestMultipleStunServersWithBadServer) {
+ ServerAddresses stun_servers;
+ stun_servers.insert(kStunAddr1);
+ stun_servers.insert(kBadAddr);
+ CreateStunPort(stun_servers);
+ EXPECT_EQ("stun", port()->Type());
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_EQ(1U, port()->Candidates().size());
+ std::string server_url = "stun:" + kBadAddr.ToString();
+ ASSERT_EQ_SIMULATED_WAIT(error_event_.url, server_url, kTimeoutMs,
+ fake_clock);
+}
+
+// Test that two candidates are allocated if the two STUN servers return
+// different mapped addresses.
+TEST_F(StunPortTest, TestTwoCandidatesWithTwoStunServersAcrossNat) {
+ const SocketAddress kStunMappedAddr1("77.77.77.77", 0);
+ const SocketAddress kStunMappedAddr2("88.77.77.77", 0);
+ stun_server_1()->set_fake_stun_addr(kStunMappedAddr1);
+ stun_server_2()->set_fake_stun_addr(kStunMappedAddr2);
+
+ ServerAddresses stun_servers;
+ stun_servers.insert(kStunAddr1);
+ stun_servers.insert(kStunAddr2);
+ CreateStunPort(stun_servers);
+ EXPECT_EQ("stun", port()->Type());
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_EQ(2U, port()->Candidates().size());
+ EXPECT_EQ(port()->Candidates()[0].relay_protocol(), "");
+ EXPECT_EQ(port()->Candidates()[1].relay_protocol(), "");
+}
+
+// Test that the stun_keepalive_lifetime is set correctly based on the network
+// type on a STUN port. Also test that it will be updated if the network type
+// changes.
+TEST_F(StunPortTest, TestStunPortGetStunKeepaliveLifetime) {
+ // Lifetime for the default (unknown) network type is `kInfiniteLifetime`.
+ CreateStunPort(kStunAddr1);
+ EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime());
+ // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetimeMs`
+ SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR);
+ EXPECT_EQ(kHighCostPortKeepaliveLifetimeMs,
+ port()->stun_keepalive_lifetime());
+
+ // Lifetime for the wifi network is `kInfiniteLifetime`.
+ SetNetworkType(rtc::ADAPTER_TYPE_WIFI);
+ CreateStunPort(kStunAddr2);
+ EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime());
+}
+
+// Test that the stun_keepalive_lifetime is set correctly based on the network
+// type on a shared STUN port (UDPPort). Also test that it will be updated
+// if the network type changes.
+TEST_F(StunPortTest, TestUdpPortGetStunKeepaliveLifetime) {
+ // Lifetime for the default (unknown) network type is `kInfiniteLifetime`.
+ CreateSharedUdpPort(kStunAddr1, nullptr);
+ EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime());
+ // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetimeMs`.
+ SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR);
+ EXPECT_EQ(kHighCostPortKeepaliveLifetimeMs,
+ port()->stun_keepalive_lifetime());
+
+ // Lifetime for the wifi network type is `kInfiniteLifetime`.
+ SetNetworkType(rtc::ADAPTER_TYPE_WIFI);
+ CreateSharedUdpPort(kStunAddr2, nullptr);
+ EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime());
+}
+
+// Test that STUN binding requests will be stopped shortly if the keep-alive
+// lifetime is short.
+TEST_F(StunPortTest, TestStunBindingRequestShortLifetime) {
+ SetKeepaliveDelay(101);
+ SetKeepaliveLifetime(100);
+ CreateStunPort(kStunAddr1);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_TRUE_SIMULATED_WAIT(!HasPendingRequest(cricket::STUN_BINDING_REQUEST),
+ 2000, fake_clock);
+}
+
+// Test that by default, the STUN binding requests will last for a long time.
+TEST_F(StunPortTest, TestStunBindingRequestLongLifetime) {
+ SetKeepaliveDelay(101);
+ CreateStunPort(kStunAddr1);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_TRUE_SIMULATED_WAIT(HasPendingRequest(cricket::STUN_BINDING_REQUEST),
+ 1000, fake_clock);
+}
+
+class MockAsyncPacketSocket : public rtc::AsyncPacketSocket {
+ public:
+ ~MockAsyncPacketSocket() = default;
+
+ MOCK_METHOD(SocketAddress, GetLocalAddress, (), (const, override));
+ MOCK_METHOD(SocketAddress, GetRemoteAddress, (), (const, override));
+ MOCK_METHOD(int,
+ Send,
+ (const void* pv, size_t cb, const rtc::PacketOptions& options),
+ (override));
+
+ MOCK_METHOD(int,
+ SendTo,
+ (const void* pv,
+ size_t cb,
+ const SocketAddress& addr,
+ const rtc::PacketOptions& options),
+ (override));
+ MOCK_METHOD(int, Close, (), (override));
+ MOCK_METHOD(State, GetState, (), (const, override));
+ MOCK_METHOD(int,
+ GetOption,
+ (rtc::Socket::Option opt, int* value),
+ (override));
+ MOCK_METHOD(int, SetOption, (rtc::Socket::Option opt, int value), (override));
+ MOCK_METHOD(int, GetError, (), (const, override));
+ MOCK_METHOD(void, SetError, (int error), (override));
+};
+
+// Test that outbound packets inherit the dscp value assigned to the socket.
+TEST_F(StunPortTest, TestStunPacketsHaveDscpPacketOption) {
+ MockAsyncPacketSocket* socket = new MockAsyncPacketSocket();
+ CreateSharedUdpPort(kStunAddr1, socket);
+ EXPECT_CALL(*socket, GetLocalAddress()).WillRepeatedly(Return(kLocalAddr));
+ EXPECT_CALL(*socket, GetState())
+ .WillRepeatedly(Return(rtc::AsyncPacketSocket::STATE_BOUND));
+ EXPECT_CALL(*socket, SetOption(_, _)).WillRepeatedly(Return(0));
+
+ // If DSCP is not set on the socket, stun packets should have no value.
+ EXPECT_CALL(*socket,
+ SendTo(_, _, _,
+ ::testing::Field(&rtc::PacketOptions::dscp,
+ ::testing::Eq(rtc::DSCP_NO_CHANGE))))
+ .WillOnce(Return(100));
+ PrepareAddress();
+
+ // Once it is set transport wide, they should inherit that value.
+ port()->SetOption(rtc::Socket::OPT_DSCP, rtc::DSCP_AF41);
+ EXPECT_CALL(*socket, SendTo(_, _, _,
+ ::testing::Field(&rtc::PacketOptions::dscp,
+ ::testing::Eq(rtc::DSCP_AF41))))
+ .WillRepeatedly(Return(100));
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+}
+
+class StunIPv6PortTestBase : public StunPortTestBase {
+ public:
+ StunIPv6PortTestBase()
+ : StunPortTestBase(rtc::Network("unittestipv6",
+ "unittestipv6",
+ kIPv6LocalAddr.ipaddr(),
+ 128),
+ kIPv6LocalAddr.ipaddr()) {
+ stun_server_ipv6_1_.reset(
+ cricket::TestStunServer::Create(ss(), kIPv6StunAddr1));
+ }
+
+ protected:
+ std::unique_ptr<cricket::TestStunServer> stun_server_ipv6_1_;
+};
+
+class StunIPv6PortTestWithRealClock : public StunIPv6PortTestBase {};
+
+class StunIPv6PortTest : public FakeClockBase, public StunIPv6PortTestBase {};
+
+// Test that we can get an address from a STUN server.
+TEST_F(StunIPv6PortTest, TestPrepareAddress) {
+ CreateStunPort(kIPv6StunAddr1);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kIPv6LocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ std::string expected_server_url = "stun:::1:5000";
+ EXPECT_EQ(port()->Candidates()[0].url(), expected_server_url);
+}
+
+// Test that we fail properly if we can't get an address.
+TEST_F(StunIPv6PortTest, TestPrepareAddressFail) {
+ CreateStunPort(kBadAddr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ EXPECT_TRUE(error());
+ EXPECT_EQ(0U, port()->Candidates().size());
+ EXPECT_EQ_SIMULATED_WAIT(error_event_.error_code,
+ cricket::SERVER_NOT_REACHABLE_ERROR, kTimeoutMs,
+ fake_clock);
+ ASSERT_NE(error_event_.error_text.find('.'), std::string::npos);
+ ASSERT_NE(
+ error_event_.address.find(kIPv6LocalAddr.HostAsSensitiveURIString()),
+ std::string::npos);
+ std::string server_url = "stun:" + kBadAddr.ToString();
+ ASSERT_EQ(error_event_.url, server_url);
+}
+
+// Test that we handle hostname lookup failures properly with a real clock.
+TEST_F(StunIPv6PortTestWithRealClock, TestPrepareAddressHostnameFail) {
+ CreateStunPort(kBadHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_WAIT(done(), kTimeoutMs);
+ EXPECT_TRUE(error());
+ EXPECT_EQ(0U, port()->Candidates().size());
+ EXPECT_EQ_WAIT(error_event_.error_code, cricket::SERVER_NOT_REACHABLE_ERROR,
+ kTimeoutMs);
+}
+
+class StunIPv6PortTestWithMockDnsResolver : public StunIPv6PortTest {
+ public:
+ StunIPv6PortTestWithMockDnsResolver()
+ : StunIPv6PortTest(), socket_factory_(ss()) {}
+
+ rtc::PacketSocketFactory* socket_factory() override {
+ return &socket_factory_;
+ }
+
+ void SetDnsResolverExpectations(
+ rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
+ socket_factory_.SetExpectations(expectations);
+ }
+
+ private:
+ rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
+};
+
+// Test that we can get an address from a STUN server specified by a hostname.
+TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostname) {
+ SetDnsResolverExpectations(
+ [](webrtc::MockAsyncDnsResolver* resolver,
+ webrtc::MockAsyncDnsResolverResult* resolver_result) {
+ EXPECT_CALL(*resolver,
+ Start(kValidHostnameAddr, /*family=*/AF_INET6, _))
+ .WillOnce([](const rtc::SocketAddress& addr, int family,
+ absl::AnyInvocable<void()> callback) { callback(); });
+
+ EXPECT_CALL(*resolver, result)
+ .WillRepeatedly(ReturnPointee(resolver_result));
+ EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0));
+ EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+ .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("::1", 5000)),
+ Return(true)));
+ });
+ CreateStunPort(kValidHostnameAddr);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kIPv6LocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_EQ(kIPv6StunCandidatePriority, port()->Candidates()[0].priority());
+}
+
+// Same as before but with a field trial that changes the priority.
+TEST_F(StunIPv6PortTestWithMockDnsResolver,
+ TestPrepareAddressHostnameWithPriorityAdjustment) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/");
+ SetDnsResolverExpectations(
+ [](webrtc::MockAsyncDnsResolver* resolver,
+ webrtc::MockAsyncDnsResolverResult* resolver_result) {
+ EXPECT_CALL(*resolver,
+ Start(kValidHostnameAddr, /*family=*/AF_INET6, _))
+ .WillOnce([](const rtc::SocketAddress& addr, int family,
+ absl::AnyInvocable<void()> callback) { callback(); });
+ EXPECT_CALL(*resolver, result)
+ .WillRepeatedly(ReturnPointee(resolver_result));
+ EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0));
+ EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+ .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("::1", 5000)),
+ Return(true)));
+ });
+ CreateStunPort(kValidHostnameAddr, &field_trials);
+ PrepareAddress();
+ EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock);
+ ASSERT_EQ(1U, port()->Candidates().size());
+ EXPECT_TRUE(kIPv6LocalAddr.EqualIPs(port()->Candidates()[0].address()));
+ EXPECT_EQ(kIPv6StunCandidatePriority + (cricket::kMaxTurnServers << 8),
+ port()->Candidates()[0].priority());
+}
+
+} // namespace