Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/libwebrtc/p2p/base/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 38 kB image not shown  

Quelle  stun_port_unittest.cc

  Sprache: C
 

/*
 *  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 <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "api/candidate.h"
#include "api/environment/environment.h"
#include "api/environment/environment_factory.h"
#include "api/field_trials.h"
#include "api/field_trials_view.h"
#include "api/packet_socket_factory.h"
#include "api/test/mock_async_dns_resolver.h"
#include "api/transport/stun.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/base/port.h"
#include "p2p/base/stun_request.h"
#include "p2p/test/mock_dns_resolving_packet_socket_factory.h"
#include "p2p/test/nat_server.h"
#include "p2p/test/nat_socket_factory.h"
#include "p2p/test/nat_types.h"
#include "p2p/test/test_stun_server.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/checks.h"
#include "rtc_base/crypto_random.h"
#include "rtc_base/dscp.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/mdns_responder_interface.h"
#include "rtc_base/net_helpers.h"
#include "rtc_base/network.h"
#include "rtc_base/network/received_packet.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/socket_factory.h"
#include "rtc_base/socket_server.h"
#include "rtc_base/thread.h"
#include "rtc_base/virtual_socket_server.h"
#include "system_wrappers/include/metrics.h"
#include "test/create_test_environment.h"
#include "test/create_test_field_trials.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/time_controller/simulated_time_controller.h"
#include "test/wait_until.h"

namespace webrtc {
namespace {

using ::testing::_;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Field;
using ::testing::IsTrue;
using ::testing::Return;
using ::testing::ReturnPointee;
using ::testing::SetArgPointee;

const SocketAddress kPrivateIP("192.168.1.12"0);
const SocketAddress kMsdnAddress("unittest-mdns-host-name.local"0);
const SocketAddress kPublicIP("212.116.91.133"0);
const SocketAddress kNatAddr(kPublicIP.ipaddr(), NAT_SERVER_UDP_PORT);
const SocketAddress kStunServerAddr1("34.38.54.120"5000);
const SocketAddress kStunServerAddr2("34.38.54.120"4000);

const SocketAddress kPrivateIPv6("2001:4860:4860::8844"0);
const SocketAddress kPublicIPv6("2002:4860:4860::8844"5000);
const SocketAddress kNatAddrIPv6(kPublicIPv6.ipaddr(), NAT_SERVER_UDP_PORT);
const SocketAddress kStunServerAddrIPv6Addr("2003:4860:4860::8844"5000);

const SocketAddress kBadAddr("0.0.0.1"5000);
const SocketAddress kIPv6BadAddr("::ffff:0:1"5000);
const SocketAddress kValidHostnameAddr("valid-hostname"5000);
const SocketAddress kBadHostnameAddr("not-a-real-hostname"5000);
// STUN timeout (with all retries) is STUN_TOTAL_TIMEOUT.
// Add some margin of error for slow bots.
constexpr TimeDelta kTimeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT + 5000);
// stun prio = 100 (srflx) << 24 | 30 (IPv4) << 8 | 256 - 1 (component)
constexpr uint32_t kStunCandidatePriority = (100 << 24) | (30 << 8) | (256 - 1);
// stun prio = 100 (srflx) << 24 | 40 (IPv6) << 8 | 256 - 1 (component)
constexpr uint32_t kIPv6StunCandidatePriority =
    (100 << 24) | (40 << 8) | (256 - 1);
constexpr uint64_t kTiebreakerDefault = 44444;

struct IPAddressTypeTestConfig {
  absl::string_view address;
  IPAddressType address_type;
};

class FakeMdnsResponder : public MdnsResponderInterface {
 public:
  void CreateNameForAddress(const IPAddress& addr,
                            NameCreatedCallback callback) override {
    callback(addr, kMsdnAddress.HostAsSensitiveURIString());
  }

  void RemoveNameForAddress(const IPAddress& addr,
                            NameRemovedCallback callback) override {}
};

class FakeMdnsResponderProvider : public MdnsResponderProvider {
 public:
  FakeMdnsResponderProvider() : mdns_responder_(new FakeMdnsResponder()) {}

  MdnsResponderInterface* GetMdnsResponder() const override {
    return mdns_responder_.get();
  }

 private:
  std::unique_ptr<MdnsResponderInterface> mdns_responder_;
};

// Base class for tests connecting a StunPort to a fake STUN server
// (StunServer).
class StunPortTest : public ::testing::Test {
 public:
  StunPortTest()
      : StunPortTest(kPrivateIP.ipaddr(),
                     {kStunServerAddr1, kStunServerAddr2},
                     kNatAddr) {}

  StunPortTest(const IPAddress address,
               const std::set<SocketAddress>& stun_server_addresses,
               const SocketAddress& nat_server_address)
      : ss_(std::make_unique<VirtualSocketServer>()),
        time_controller_(Timestamp::Zero(), ss_.get()),
        env_(CreateTestEnvironment({.time = &time_controller_})),
        network_thread_(time_controller_.GetMainThread()),
        nat_factory_(ss_.get(), nat_server_address, nat_server_address),
        nat_socket_factory_(&nat_factory_),
        mdns_responder_provider_(new FakeMdnsResponderProvider()),
        done_(false),
        error_(false),
        stun_keepalive_delay_(TimeDelta::Millis(1)) {
    nat_server_ = CreateNatServer(nat_server_address, NAT_OPEN_CONE);
    network_ = MakeNetwork(address);
    RTC_CHECK(address.family() == nat_server_address.family());
    for (const auto& addr : stun_server_addresses) {
      RTC_CHECK(addr.family() == address.family());
      stun_servers_.push_back(
          TestStunServer::Create(env_, addr, *ss_, *network_thread_));
    }
  }

  std::unique_ptr<NATServer> CreateNatServer(const SocketAddress& addr,
                                             NATType type) {
    return std::make_unique<NATServer>(env_, type, *network_thread_, ss_.get(),
                                       addr, addr, *network_thread_, ss_.get(),
                                       addr);
  }

  virtual PacketSocketFactory* socket_factory() { return &nat_socket_factory_; }

  SocketServer* ss() const { return ss_.get(); }
  UDPPort* port() const { return stun_port_.get(); }
  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(AdapterType adapter_type) {
    network_->set_type(adapter_type);
  }

  void CreateStunPort(const SocketAddress& server_addr,
                      const FieldTrialsView* field_trials = nullptr) {
    ServerAddresses stun_servers;
    stun_servers.insert(server_addr);
    CreateStunPort(stun_servers, field_trials);
  }

  void CreateStunPort(const ServerAddresses& stun_servers,
                      const FieldTrialsView* field_trials = nullptr) {
    // Overwrite field trials if provided.
    EnvironmentFactory env_factory(env_);
    env_factory.Set(field_trials);
    Environment env = env_factory.Create();

    stun_port_ =
        StunPort::Create({.env = env,
                          .network_thread = network_thread_,
                          .socket_factory = socket_factory(),
                          .network = network_,
                          .ice_username_fragment = CreateRandomString(16),
                          .ice_password = CreateRandomString(22)},
                         00, stun_servers, std::nullopt);
    stun_port_->SetIceTiebreaker(kTiebreakerDefault);
    stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_);
    // If `stun_keepalive_lifetime_` is not set, let the stun port choose its
    // lifetime from the network type.
    if (stun_keepalive_lifetime_.has_value()) {
      stun_port_->set_stun_keepalive_lifetime(*stun_keepalive_lifetime_);
    }
    stun_port_->SubscribePortComplete(
        this, [this](Port* port) { OnPortComplete(port); });
    stun_port_->SubscribePortError(this,
                                   [this](Port* port) { OnPortError(port); });

    stun_port_->SubscribeCandidateError(
        this, [this](Port* port, const IceCandidateErrorEvent& event) {
          OnCandidateError(port, event);
        });
  }

  void CreateSharedUdpPort(const SocketAddress& server_addr,
                           std::unique_ptr<AsyncPacketSocket> socket) {
    // Destroy existing stun_port_, if any, before overwriting socket_.
    if (stun_port_) {
      stun_port_ = nullptr;
    }
    if (socket) {
      socket_ = std::move(socket);
    } else {
      socket_ = socket_factory()->CreateUdpSocket(
          env_, SocketAddress(kPrivateIP.ipaddr(), 0), 00);
    }
    ASSERT_TRUE(socket_ != nullptr);
    socket_->RegisterReceivedPacketCallback(
        [&](AsyncPacketSocket* socket, const ReceivedIpPacket& packet) {
          OnReadPacket(socket, packet);
        });
    ServerAddresses stun_servers;
    stun_servers.insert(server_addr);
    stun_port_ =
        UDPPort::Create({.env = env_,
                         .network_thread = network_thread_,
                         .socket_factory = socket_factory(),
                         .network = network_,
                         .ice_username_fragment = CreateRandomString(16),
                         .ice_password = CreateRandomString(22)},
                        socket_.get(), false, std::nullopt);
    stun_port_->set_server_addresses(stun_servers);
    ASSERT_TRUE(stun_port_ != nullptr);
    stun_port_->SetIceTiebreaker(kTiebreakerDefault);
    stun_port_->SubscribePortComplete(
        this, [this](Port* port) { OnPortComplete(port); });
    stun_port_->SubscribePortError(this,
                                   [this](Port* port) { OnPortError(port); });
  }

  void PrepareAddress() {
    stun_port_->PrepareAddress();
    time_controller_.AdvanceTime(TimeDelta::Zero());
  }

  void OnReadPacket(AsyncPacketSocket* socket, const ReceivedIpPacket& packet) {
    stun_port_->HandleIncomingPacket(socket, packet);
  }

  void SendData(const char* data, size_t len) {
    stun_port_->HandleIncomingPacket(
        socket_.get(),
        ReceivedIpPacket::CreateFromLegacy(data, len, /* packet_time_us */ -1,
                                           SocketAddress("22.22.22.22"0)));
  }

  void EnableMdnsObfuscation() {
    network_->set_mdns_responder_provider(mdns_responder_provider_.get());
  }

 protected:
  void OnPortComplete(Port* /* port */) {
    ASSERT_FALSE(done_);
    done_ = true;
    error_ = false;
  }
  void OnPortError(Port* /* port */) {
    done_ = true;
    error_ = true;
  }
  void OnCandidateError(Port* /* port */, const IceCandidateErrorEvent& event) {
    error_event_ = event;
  }
  void SetKeepaliveDelay(TimeDelta delay) { stun_keepalive_delay_ = delay; }

  void SetKeepaliveLifetime(TimeDelta lifetime) {
    stun_keepalive_lifetime_ = lifetime;
  }

  Network* MakeNetwork(const IPAddress& addr) {
    networks_.emplace_back(
        std::make_unique<Network>("unittest""unittest", addr, 32));
    networks_.back()->AddIP(addr);
    return networks_.back().get();
  }

  TestStunServer* stun_server_1() { return stun_servers_[0].get(); }
  TestStunServer* stun_server_2() { return stun_servers_[1].get(); }

  Thread& thread() { return *network_thread_; }
  SocketFactory* nat_factory() { return &nat_factory_; }

 protected:
  std::unique_ptr<VirtualSocketServer> ss_;
  GlobalSimulatedTimeController time_controller_;
  const Environment env_;
  std::vector<std::unique_ptr<Network>> networks_;
  Network* network_;

  Thread* network_thread_;
  NATSocketFactory nat_factory_;
  BasicPacketSocketFactory nat_socket_factory_;
  // Note that stun_port_ can refer to socket_, so must be destroyed
  // before it.
  std::unique_ptr<AsyncPacketSocket> socket_;
  std::unique_ptr<UDPPort> stun_port_;
  std::vector<TestStunServer::StunServerPtr> stun_servers_;
  std::unique_ptr<MdnsResponderProvider> mdns_responder_provider_;
  std::unique_ptr<NATServer> nat_server_;
  bool done_;
  bool error_;
  TimeDelta stun_keepalive_delay_;
  std::optional<TimeDelta> stun_keepalive_lifetime_;

 protected:
  IceCandidateErrorEvent error_event_;
};

// Test that we can create a STUN port.
TEST_F(StunPortTest, TestCreateStunPort) {
  CreateStunPort(kStunServerAddr1);
  EXPECT_EQ(IceCandidateType::kSrflx, port()->Type());
  EXPECT_EQ(0U, port()->Candidates().size());
}

// Test that we can create a UDP port.
TEST_F(StunPortTest, TestCreateUdpPort) {
  CreateSharedUdpPort(kStunServerAddr1, nullptr);
  EXPECT_EQ(IceCandidateType::kHost, port()->Type());
  EXPECT_EQ(0U, port()->Candidates().size());
}

// Test that we can get an address from a STUN server.
TEST_F(StunPortTest, TestPrepareAddress) {
  CreateStunPort(kStunServerAddr1);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address()));
  std::string expected_server_url = "stun:" + kStunServerAddr1.ToString();
  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(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_TRUE(error());
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE);
  EXPECT_NE(error_event_.error_text.find('.'), std::string::npos);
  EXPECT_NE(error_event_.address.find(kPrivateIP.HostAsSensitiveURIString()),
            std::string::npos);
  std::string server_url = "stun:" + kBadAddr.ToString();
  EXPECT_EQ(error_event_.url, server_url);
}

// Test that we fail without emitting an error if we try to get an address from
// a STUN server with a different address family. IPv4 local, IPv6 STUN.
TEST_F(StunPortTest, TestServerAddressFamilyMismatch) {
  CreateStunPort(kStunServerAddrIPv6Addr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_TRUE(error());
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(0, error_event_.error_code);
}

class StunPortWithMockDnsResolverTest : public StunPortTest {
 public:
  StunPortWithMockDnsResolverTest()
      : StunPortTest(), socket_factory_(nat_factory()) {}

  PacketSocketFactory* socket_factory() override { return &socket_factory_; }

  void SetDnsResolverExpectations(
      MockDnsResolvingPacketSocketFactory::Expectations expectations) {
    socket_factory_.SetExpectations(expectations);
  }

 private:
  MockDnsResolvingPacketSocketFactory socket_factory_;
};

// Test that we can get an address from a STUN server specified by a hostname.
TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostname) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _))
        .WillOnce([](const 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>(kStunServerAddr1), Return(true)));
  });
  CreateStunPort(kValidHostnameAddr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address()));
  EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority());
}

TEST_F(StunPortWithMockDnsResolverTest,
       TestPrepareAddressHostnameWithPriorityAdjustment) {
  FieldTrials field_trials = CreateTestFieldTrials(
      "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/");
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _))
        .WillOnce([](const 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>(kStunServerAddr1), Return(true)));
  });
  CreateStunPort(kValidHostnameAddr, &field_trials);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address()));
  EXPECT_EQ(kStunCandidatePriority + (kMaxTurnServers << 8),
            port()->Candidates()[0].priority());
}

// Test that we handle hostname lookup failures properly.
// Test that we fail to get an address from a STUN server specified by an
// invalid hostname.
TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostnameFail) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET, _))
        .WillOnce([](const SocketAddress& /* addr */, int /* family */,
                     absl::AnyInvocable<void()> callback) { callback(); });

    EXPECT_CALL(*resolver, result)
        .WillRepeatedly(ReturnPointee(resolver_result));
    EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2));
  });
  CreateStunPort(kBadHostnameAddr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return error(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE);
}

// This test verifies keepalive response messages don't result in
// additional candidate generation.
TEST_F(StunPortTest, TestKeepAliveResponse) {
  SetKeepaliveDelay(TimeDelta::Millis(500));
  CreateStunPort(kStunServerAddr1);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address()));
  time_controller_.AdvanceTime(TimeDelta::Millis(1000));
  EXPECT_EQ(1U, port()->Candidates().size());
}

// Test that a local candidate can be generated using a shared socket.
TEST_F(StunPortTest, TestSharedSocketPrepareAddress) {
  CreateSharedUdpPort(kStunServerAddr1, nullptr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(2U, port()->Candidates().size());
  EXPECT_EQ(port()->Candidates()[0].type(), IceCandidateType::kHost);
  EXPECT_TRUE(kPrivateIP.EqualIPs(port()->Candidates()[0].address()));
  EXPECT_EQ(port()->Candidates()[1].type(), IceCandidateType::kSrflx);
  EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[1].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(StunPortWithMockDnsResolverTest,
       TestSharedSocketPrepareAddressInvalidHostname) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET, _))
        .WillOnce([](const SocketAddress& /* addr */, int /* family */,
                     absl::AnyInvocable<void()> callback) { callback(); });

    EXPECT_CALL(*resolver, result)
        .WillRepeatedly(ReturnPointee(resolver_result));
    EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2));
  });
  CreateSharedUdpPort(kBadHostnameAddr, nullptr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPrivateIP.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 Port.";
  SendData(data.c_str(), data.length());
  // No crash is success.
}

// 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(kStunServerAddr1, nullptr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(2U, port()->Candidates().size());

  // 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].is_local()) {
    EXPECT_EQ(kMsdnAddress.HostAsSensitiveURIString(),
              port()->Candidates()[0].address().HostAsSensitiveURIString());
    EXPECT_TRUE(port()->Candidates()[1].is_stun());
    EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[1].address()));
  } else {
    EXPECT_TRUE(port()->Candidates()[0].is_stun());
    EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address()));
    EXPECT_TRUE(port()->Candidates()[1].is_local());
    EXPECT_EQ(kMsdnAddress.HostAsSensitiveURIString(),
              port()->Candidates()[1].address().HostAsSensitiveURIString());
  }
}

// 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(kStunServerAddr1);
  stun_servers.insert(kStunServerAddr2);
  CreateStunPort(stun_servers);
  EXPECT_EQ(IceCandidateType::kSrflx, port()->Type());
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  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(kStunServerAddr1);
  stun_servers.insert(kBadAddr);
  CreateStunPort(stun_servers);
  EXPECT_EQ(IceCandidateType::kSrflx, port()->Type());
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_EQ(1U, port()->Candidates().size());
  std::string server_url = "stun:" + kBadAddr.ToString();
  EXPECT_EQ(error_event_.url, server_url);
}

// 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(kStunServerAddr1);
  stun_servers.insert(kStunServerAddr2);
  CreateStunPort(stun_servers);
  EXPECT_EQ(IceCandidateType::kSrflx, port()->Type());
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  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 infinite.
  CreateStunPort(kStunServerAddr1);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity());
  // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetime`
  SetNetworkType(ADAPTER_TYPE_CELLULAR);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), kHighCostPortKeepaliveLifetime);

  // Lifetime for the wifi network is infinite.
  SetNetworkType(ADAPTER_TYPE_WIFI);
  CreateStunPort(kStunServerAddr2);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity());
}

// 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 infinite.
  CreateSharedUdpPort(kStunServerAddr1, nullptr);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity());
  // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetime`.
  SetNetworkType(ADAPTER_TYPE_CELLULAR);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), kHighCostPortKeepaliveLifetime);

  // Lifetime for the wifi network type is infinite.
  SetNetworkType(ADAPTER_TYPE_WIFI);
  CreateSharedUdpPort(kStunServerAddr2, nullptr);
  EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity());
}

// Test that STUN binding requests will be stopped shortly if the keep-alive
// lifetime is short.
TEST_F(StunPortTest, TestStunBindingRequestShortLifetime) {
  SetKeepaliveDelay(TimeDelta::Millis(101));
  SetKeepaliveLifetime(TimeDelta::Millis(100));
  CreateStunPort(kStunServerAddr1);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_TRUE(
      WaitUntil([&] { return !HasPendingRequest(STUN_BINDING_REQUEST); },
                {.timeout = kTimeout, .clock = &time_controller_}));
}

// Test that by default, the STUN binding requests will last for a long time.
TEST_F(StunPortTest, TestStunBindingRequestLongLifetime) {
  SetKeepaliveDelay(TimeDelta::Millis(101));
  CreateStunPort(kStunServerAddr1);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_TRUE(WaitUntil([&] { return HasPendingRequest(STUN_BINDING_REQUEST); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
}

class StunPortIPAddressTypeMetricsTest
    : public StunPortWithMockDnsResolverTest,
      public ::testing::WithParamInterface<IPAddressTypeTestConfig> {};

TEST_P(StunPortIPAddressTypeMetricsTest, TestIPAddressTypeMetrics) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(SocketAddress("localhost"5000),
                                 /*family=*/AF_INET, _))
        .WillOnce([](const 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)));
  });

  metrics::Reset();

  CreateStunPort({GetParam().address, 5000});
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));

  auto samples =
      metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType");
  ASSERT_EQ(samples.size(), 1u);
  EXPECT_EQ(samples[static_cast<int>(GetParam().address_type)], 1);
}

const IPAddressTypeTestConfig kAllIPAddressTypeTestConfigs[] = {
    {.address = "127.0.0.1", .address_type = IPAddressType::kLoopback},
    {.address = "localhost", .address_type = IPAddressType::kLoopback},
    {.address = "10.0.0.3", .address_type = IPAddressType::kPrivate},
    {.address = "1.1.1.1", .address_type = IPAddressType::kPublic},
};

INSTANTIATE_TEST_SUITE_P(All,
                         StunPortIPAddressTypeMetricsTest,
                         ::testing::ValuesIn(kAllIPAddressTypeTestConfigs));

class MockAsyncPacketSocket : public AsyncPacketSocket {
 public:
  ~MockAsyncPacketSocket() override = default;

  MOCK_METHOD(SocketAddress, GetLocalAddress, (), (const, override));
  MOCK_METHOD(SocketAddress, GetRemoteAddress, (), (const, override));
  MOCK_METHOD(int,
              Send,
              (const void* pv,
               size_t cb,
               const AsyncSocketPacketOptions& options),
              (override));

  MOCK_METHOD(int,
              SendTo,
              (const void* pv,
               size_t cb,
               const SocketAddress& addr,
               const AsyncSocketPacketOptions& options),
              (override));
  MOCK_METHOD(int, Close, (), (override));
  MOCK_METHOD(State, GetState, (), (const, override));
  MOCK_METHOD(int, GetOption, (Socket::Option opt, int* value), (override));
  MOCK_METHOD(int, SetOption, (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) {
  auto mocked_socket = std::make_unique<MockAsyncPacketSocket>();
  MockAsyncPacketSocket& socket = *mocked_socket;
  CreateSharedUdpPort(kStunServerAddr1, std::move(mocked_socket));

  EXPECT_CALL(socket, GetLocalAddress).WillRepeatedly(Return(kPrivateIP));
  EXPECT_CALL(socket, GetState)
      .WillRepeatedly(Return(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(_, _, _,
                             Field(&AsyncSocketPacketOptions::dscp,
                                   Eq(DSCP_NO_CHANGE))))
      .WillOnce(Return(100));
  PrepareAddress();
  port()->request_manager().Clear();

  // Once it is set transport wide, they should inherit that value.
  port()->SetOption(Socket::OPT_DSCP, DSCP_AF41);
  EXPECT_CALL(
      socket,
      SendTo(_, _, _, Field(&AsyncSocketPacketOptions::dscp, Eq(DSCP_AF41))))
      .WillRepeatedly(Return(100));

  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
}

class StunIPv6PortTest : public StunPortTest {
 public:
  StunIPv6PortTest()
      : StunPortTest(kPrivateIPv6.ipaddr(),
                     {kStunServerAddrIPv6Addr},
                     kNatAddrIPv6) {}
};

// Test that we can get an address from a STUN server.
TEST_F(StunIPv6PortTest, TestPrepareAddress) {
  CreateStunPort(kStunServerAddrIPv6Addr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPublicIPv6.EqualIPs(port()->Candidates()[0].address()));
  std::string expected_server_url = "stun:2003:4860:4860::8844: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(kIPv6BadAddr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_TRUE(error());
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE);
  EXPECT_NE(error_event_.error_text.find('.'), std::string::npos);
  EXPECT_NE(error_event_.address.find(kPrivateIPv6.HostAsSensitiveURIString()),
            std::string::npos);
  std::string server_url = "stun:" + kIPv6BadAddr.ToString();
  EXPECT_EQ(error_event_.url, server_url);
}

// Test that we fail without emitting an error if we try to get an address from
// a STUN server with a different address family. IPv6 local, IPv4 STUN.
TEST_F(StunIPv6PortTest, TestServerAddressFamilyMismatch) {
  CreateStunPort(kStunServerAddr1);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return error(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(0, error_event_.error_code);
}

class StunIPv6PortTestWithMockDnsResolver : public StunIPv6PortTest {
 public:
  StunIPv6PortTestWithMockDnsResolver()
      : StunIPv6PortTest(), socket_factory_(ss()) {}

  PacketSocketFactory* socket_factory() override { return &socket_factory_; }

  void SetDnsResolverExpectations(
      MockDnsResolvingPacketSocketFactory::Expectations expectations) {
    socket_factory_.SetExpectations(expectations);
  }

 private:
  MockDnsResolvingPacketSocketFactory socket_factory_;
};

// Test that we fail to get an address from a STUN server specified by an
// invalid hostname.
TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostnameFail) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET6, _))
        .WillOnce([](const SocketAddress& /* addr */, int /* family */,
                     absl::AnyInvocable<void()> callback) { callback(); });

    EXPECT_CALL(*resolver, result)
        .WillRepeatedly(ReturnPointee(resolver_result));
    EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2));
  });
  CreateStunPort(kBadHostnameAddr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return error(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  EXPECT_EQ(0U, port()->Candidates().size());
  EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE);
}

// Test that we can get an address from a STUN server specified by a hostname.
TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostname) {
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET6, _))
        .WillOnce([](const 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>(kStunServerAddrIPv6Addr), Return(true)));
  });
  CreateStunPort(kValidHostnameAddr);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPrivateIPv6.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) {
  FieldTrials field_trials = CreateTestFieldTrials(
      "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/");
  SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver,
                                MockAsyncDnsResolverResult* resolver_result) {
    EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET6, _))
        .WillOnce([](const 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>(kStunServerAddrIPv6Addr), Return(true)));
  });
  CreateStunPort(kValidHostnameAddr, &field_trials);
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));
  ASSERT_EQ(1U, port()->Candidates().size());
  EXPECT_TRUE(kPrivateIPv6.EqualIPs(port()->Candidates()[0].address()));
  EXPECT_EQ(kIPv6StunCandidatePriority + (kMaxTurnServers << 8),
            port()->Candidates()[0].priority());
}

class StunIPv6PortIPAddressTypeMetricsTest
    : public StunIPv6PortTestWithMockDnsResolver,
      public ::testing::WithParamInterface<IPAddressTypeTestConfig> {};

TEST_P(StunIPv6PortIPAddressTypeMetricsTest, TestIPAddressTypeMetrics) {
  metrics::Reset();

  CreateStunPort({GetParam().address, 5000});
  PrepareAddress();
  EXPECT_TRUE(WaitUntil([this] { return done(); },
                        {.timeout = kTimeout, .clock = &time_controller_}));

  auto samples =
      metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType");
  ASSERT_EQ(samples.size(), 1u);
  EXPECT_EQ(samples[static_cast<int>(GetParam().address_type)], 1);
}

const IPAddressTypeTestConfig kAllIPv6AddressTypeTestConfigs[] = {
    {.address = "::1", .address_type = IPAddressType::kLoopback},
    {.address = "fd00:4860:4860::8844",
     .address_type = IPAddressType::kPrivate},
    {.address = "2001:4860:4860::8888", .address_type = IPAddressType::kPublic},
};

INSTANTIATE_TEST_SUITE_P(All,
                         StunIPv6PortIPAddressTypeMetricsTest,
                         ::testing::ValuesIn(kAllIPv6AddressTypeTestConfigs));

}  // namespace
}  // namespace webrtc

Messung V0.5 in Prozent
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.