Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  port_unittest.cc   Sprache: C

 
/*
 *  Copyright 2004 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/port.h"

#include <string.h>

#include <cstdint>
#include <limits>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/candidate.h"
#include "api/packet_socket_factory.h"
#include "api/transport/stun.h"
#include "api/units/time_delta.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
#include "p2p/base/port_interface.h"
#include "p2p/base/stun_port.h"
#include "p2p/base/stun_server.h"
#include "p2p/base/tcp_port.h"
#include "p2p/base/test_stun_server.h"
#include "p2p/base/test_turn_server.h"
#include "p2p/base/transport_description.h"
#include "p2p/base/turn_port.h"
#include "p2p/base/turn_server.h"
#include "p2p/client/relay_port_factory_interface.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/buffer.h"
#include "rtc_base/byte_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/crypto_random.h"
#include "rtc_base/dscp.h"
#include "rtc_base/fake_clock.h"
#include "rtc_base/gunit.h"
#include "rtc_base/logging.h"
#include "rtc_base/nat_server.h"
#include "rtc_base/nat_socket_factory.h"
#include "rtc_base/nat_types.h"
#include "rtc_base/net_helper.h"
#include "rtc_base/network.h"
#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_adapters.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/thread.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/virtual_socket_server.h"
#include "test/gtest.h"
#include "test/scoped_key_value_config.h"

using rtc::AsyncListenSocket;
using rtc::AsyncPacketSocket;
using rtc::ByteBufferReader;
using rtc::ByteBufferWriter;
using rtc::NAT_ADDR_RESTRICTED;
using rtc::NAT_OPEN_CONE;
using rtc::NAT_PORT_RESTRICTED;
using rtc::NAT_SYMMETRIC;
using rtc::NATType;
using rtc::PacketSocketFactory;
using rtc::Socket;
using rtc::SocketAddress;
using webrtc::IceCandidateType;

namespace cricket {
namespace {

constexpr int kDefaultTimeout = 3000;
constexpr int kShortTimeout = 1000;
constexpr int kMaxExpectedSimulatedRtt = 200;
const SocketAddress kLocalAddr1("192.168.1.2", 0);
const SocketAddress kLocalAddr2("192.168.1.3", 0);
const SocketAddress kLinkLocalIPv6Addr("fe80::aabb:ccff:fedd:eeff", 0);
const SocketAddress kNatAddr1("77.77.77.77", rtc::NAT_SERVER_UDP_PORT);
const SocketAddress kNatAddr2("88.88.88.88", rtc::NAT_SERVER_UDP_PORT);
const SocketAddress kStunAddr("99.99.99.1", STUN_SERVER_PORT);
const SocketAddress kTurnUdpIntAddr("99.99.99.4", STUN_SERVER_PORT);
const SocketAddress kTurnTcpIntAddr("99.99.99.4", 5010);
const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0);
const RelayCredentials kRelayCredentials("test""test");

// TODO(?): Update these when RFC5245 is completely supported.
// Magic value of 30 is from RFC3484, for IPv4 addresses.
const uint32_t kDefaultPrflxPriority = ICE_TYPE_PREFERENCE_PRFLX << 24 |
                                       30 << 8 |
                                       (256 - ICE_CANDIDATE_COMPONENT_DEFAULT);

constexpr int kTiebreaker1 = 11111;
constexpr int kTiebreaker2 = 22222;
constexpr int kTiebreakerDefault = 44444;

const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

Candidate GetCandidate(Port* port) {
  RTC_DCHECK_GE(port->Candidates().size(), 1);
  return port->Candidates()[0];
}

SocketAddress GetAddress(Port* port) {
  return GetCandidate(port).address();
}

std::unique_ptr<IceMessage> CopyStunMessage(const IceMessage& src) {
  auto dst = std::make_unique<IceMessage>();
  ByteBufferWriter buf;
  src.Write(&buf);
  ByteBufferReader read_buf(buf);
  dst->Read(&read_buf);
  return dst;
}

bool WriteStunMessage(const StunMessage& msg, ByteBufferWriter* buf) {
  buf->Resize(0);  // clear out any existing buffer contents
  return msg.Write(buf);
}

}  // namespace

// Stub port class for testing STUN generation and processing.
class TestPort : public Port {
 public:
  TestPort(const PortParametersRef& args, uint16_t min_port, uint16_t max_port)
      : Port(args, IceCandidateType::kHost, min_port, max_port) {}
  ~TestPort() {}

  // Expose GetStunMessage so that we can test it.
  using cricket::Port::GetStunMessage;

  // The last StunMessage that was sent on this Port.
  rtc::ArrayView<const uint8_t> last_stun_buf() {
    if (!last_stun_buf_)
      return rtc::ArrayView<const uint8_t>();
    return *last_stun_buf_;
  }
  IceMessage* last_stun_msg() { return last_stun_msg_.get(); }
  int last_stun_error_code() {
    int code = 0;
    if (last_stun_msg_) {
      const StunErrorCodeAttribute* error_attr = last_stun_msg_->GetErrorCode();
      if (error_attr) {
        code = error_attr->code();
      }
    }
    return code;
  }

  virtual void PrepareAddress() {
    // Act as if the socket was bound to the best IP on the network, to the
    // first port in the allowed range.
    rtc::SocketAddress addr(Network()->GetBestIP(), min_port());
    AddAddress(addr, addr, rtc::SocketAddress(), "udp""""", type(),
               ICE_TYPE_PREFERENCE_HOST, 0, ""true);
  }

  virtual bool SupportsProtocol(absl::string_view protocol) const {
    return true;
  }

  virtual ProtocolType GetProtocol() const { return PROTO_UDP; }

  // Exposed for testing candidate building.
  void AddCandidateAddress(const rtc::SocketAddress& addr) {
    AddAddress(addr, addr, rtc::SocketAddress(), "udp""""", type(),
               type_preference_, 0, ""false);
  }
  void AddCandidateAddress(const rtc::SocketAddress& addr,
                           const rtc::SocketAddress& base_address,
                           IceCandidateType type,
                           int type_preference,
                           bool final) {
    AddAddress(addr, base_address, rtc::SocketAddress(), "udp""""", type,
               type_preference, 0, "", final);
  }

  virtual Connection* CreateConnection(const Candidate& remote_candidate,
                                       CandidateOrigin origin) {
    Connection* conn = new ProxyConnection(NewWeakPtr(), 0, remote_candidate);
    AddOrReplaceConnection(conn);
    // Set use-candidate attribute flag as this will add USE-CANDIDATE attribute
    // in STUN binding requests.
    conn->set_use_candidate_attr(true);
    return conn;
  }
  virtual int SendTo(const void* data,
                     size_t size,
                     const rtc::SocketAddress& addr,
                     const rtc::PacketOptions& options,
                     bool payload) {
    if (!payload) {
      auto msg = std::make_unique<IceMessage>();
      auto buf = std::make_unique<rtc::BufferT<uint8_t>>(
          static_cast<const char*>(data), size);
      ByteBufferReader read_buf(*buf);
      if (!msg->Read(&read_buf)) {
        return -1;
      }
      last_stun_buf_ = std::move(buf);
      last_stun_msg_ = std::move(msg);
    }
    return static_cast<int>(size);
  }
  virtual int SetOption(rtc::Socket::Option opt, int value) { return 0; }
  virtual int GetOption(rtc::Socket::Option opt, int* value) { return -1; }
  virtual int GetError() { return 0; }
  void Reset() {
    last_stun_buf_.reset();
    last_stun_msg_.reset();
  }
  void set_type_preference(int type_preference) {
    type_preference_ = type_preference;
  }

 private:
  void OnSentPacket(rtc::AsyncPacketSocket* socket,
                    const rtc::SentPacket& sent_packet) {
    PortInterface::SignalSentPacket(sent_packet);
  }
  std::unique_ptr<rtc::BufferT<uint8_t>> last_stun_buf_;
  std::unique_ptr<IceMessage> last_stun_msg_;
  int type_preference_ = 0;
};

bool GetStunMessageFromBufferWriter(TestPort* port,
                                    ByteBufferWriter* buf,
                                    const rtc::SocketAddress& addr,
                                    std::unique_ptr<IceMessage>* out_msg,
                                    std::string* out_username) {
  return port->GetStunMessage(reinterpret_cast<const char*>(buf->Data()),
                              buf->Length(), addr, out_msg, out_username);
}

static void SendPingAndReceiveResponse(Connection* lconn,
                                       TestPort* lport,
                                       Connection* rconn,
                                       TestPort* rport,
                                       rtc::ScopedFakeClock* clock,
                                       int64_t ms) {
  lconn->Ping(rtc::TimeMillis());
  ASSERT_TRUE_WAIT(lport->last_stun_msg(), kDefaultTimeout);
  ASSERT_GT(lport->last_stun_buf().size(), 0u);
  rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
                                          rtc::SocketAddress(), std::nullopt));

  clock->AdvanceTime(webrtc::TimeDelta::Millis(ms));
  ASSERT_TRUE_WAIT(rport->last_stun_msg(), kDefaultTimeout);
  ASSERT_GT(rport->last_stun_buf().size(), 0u);
  lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
                                          rtc::SocketAddress(), std::nullopt));
}

class TestChannel : public sigslot::has_slots<> {
 public:
  // Takes ownership of `p1` (but not `p2`).
  explicit TestChannel(std::unique_ptr<Port> p1) : port_(std::move(p1)) {
    port_->SignalPortComplete.connect(this, &TestChannel::OnPortComplete);
    port_->SignalUnknownAddress.connect(this, &TestChannel::OnUnknownAddress);
    port_->SubscribePortDestroyed(
        [this](PortInterface* port) { OnSrcPortDestroyed(port); });
  }

  ~TestChannel() { Stop(); }

  int complete_count() { return complete_count_; }
  Connection* conn() { return conn_; }
  const SocketAddress& remote_address() { return remote_address_; }
  const std::string remote_fragment() { return remote_frag_; }

  void Start() { port_->PrepareAddress(); }
  void CreateConnection(const Candidate& remote_candidate) {
    RTC_DCHECK(!conn_);
    conn_ = port_->CreateConnection(remote_candidate, Port::ORIGIN_MESSAGE);
    IceMode remote_ice_mode =
        (ice_mode_ == ICEMODE_FULL) ? ICEMODE_LITE : ICEMODE_FULL;
    conn_->set_use_candidate_attr(remote_ice_mode == ICEMODE_FULL);
    conn_->SignalStateChange.connect(this,
                                     &TestChannel::OnConnectionStateChange);
    conn_->SignalDestroyed.connect(this, &TestChannel::OnDestroyed);
    conn_->SignalReadyToSend.connect(this,
                                     &TestChannel::OnConnectionReadyToSend);
    connection_ready_to_send_ = false;
  }

  void OnConnectionStateChange(Connection* conn) {
    if (conn->write_state() == Connection::STATE_WRITABLE) {
      conn->set_use_candidate_attr(true);
      nominated_ = true;
    }
  }
  void AcceptConnection(const Candidate& remote_candidate) {
    if (conn_) {
      conn_->SignalDestroyed.disconnect(this);
      conn_ = nullptr;
    }
    ASSERT_TRUE(remote_request_.get() != NULL);
    Candidate c = remote_candidate;
    c.set_address(remote_address_);
    conn_ = port_->CreateConnection(c, Port::ORIGIN_MESSAGE);
    conn_->SignalDestroyed.connect(this, &TestChannel::OnDestroyed);
    conn_->SendStunBindingResponse(remote_request_.get());
    remote_request_.reset();
  }
  void Ping() { Ping(0); }
  void Ping(int64_t now) { conn_->Ping(now); }
  void Stop() {
    if (conn_) {
      port_->DestroyConnection(conn_);
      conn_ = nullptr;
    }
  }

  void OnPortComplete(Port* port) { complete_count_++; }
  void SetIceMode(IceMode ice_mode) { ice_mode_ = ice_mode; }

  int SendData(const char* data, size_t len) {
    rtc::PacketOptions options;
    return conn_->Send(data, len, options);
  }

  void OnUnknownAddress(PortInterface* port,
                        const SocketAddress& addr,
                        ProtocolType proto,
                        IceMessage* msg,
                        const std::string& rf,
                        bool /*port_muxed*/) {
    ASSERT_EQ(port_.get(), port);
    if (!remote_address_.IsNil()) {
      ASSERT_EQ(remote_address_, addr);
    }
    const cricket::StunUInt32Attribute* priority_attr =
        msg->GetUInt32(STUN_ATTR_PRIORITY);
    const cricket::StunByteStringAttribute* mi_attr =
        msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
    const cricket::StunUInt32Attribute* fingerprint_attr =
        msg->GetUInt32(STUN_ATTR_FINGERPRINT);
    EXPECT_TRUE(priority_attr != NULL);
    EXPECT_TRUE(mi_attr != NULL);
    EXPECT_TRUE(fingerprint_attr != NULL);
    remote_address_ = addr;
    remote_request_ = CopyStunMessage(*msg);
    remote_frag_ = rf;
  }

  void OnDestroyed(Connection* conn) {
    ASSERT_EQ(conn_, conn);
    RTC_LOG(LS_INFO) << "OnDestroy connection " << conn << " deleted";
    conn_ = nullptr;
    // When the connection is destroyed, also clear these fields so future
    // connections are possible.
    remote_request_.reset();
    remote_address_.Clear();
  }

  void OnSrcPortDestroyed(PortInterface* port) {
    Port* destroyed_src = port_.release();
    ASSERT_EQ(destroyed_src, port);
  }

  Port* port() { return port_.get(); }

  bool nominated() const { return nominated_; }

  void set_connection_ready_to_send(bool ready) {
    connection_ready_to_send_ = ready;
  }
  bool connection_ready_to_send() const { return connection_ready_to_send_; }

 private:
  // ReadyToSend will only issue after a Connection recovers from ENOTCONN
  void OnConnectionReadyToSend(Connection* conn) {
    ASSERT_EQ(conn, conn_);
    connection_ready_to_send_ = true;
  }

  IceMode ice_mode_ = ICEMODE_FULL;
  std::unique_ptr<Port> port_;

  int complete_count_ = 0;
  Connection* conn_ = nullptr;
  SocketAddress remote_address_;
  std::unique_ptr<StunMessage> remote_request_;
  std::string remote_frag_;
  bool nominated_ = false;
  bool connection_ready_to_send_ = false;
};

class PortTest : public ::testing::Test, public sigslot::has_slots<> {
 public:
  PortTest()
      : ss_(new rtc::VirtualSocketServer()),
        main_(ss_.get()),
        socket_factory_(ss_.get()),
        nat_factory1_(ss_.get(), kNatAddr1, SocketAddress()),
        nat_factory2_(ss_.get(), kNatAddr2, SocketAddress()),
        nat_socket_factory1_(&nat_factory1_),
        nat_socket_factory2_(&nat_factory2_),
        stun_server_(TestStunServer::Create(ss_.get(), kStunAddr, main_)),
        turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr),
        username_(rtc::CreateRandomString(ICE_UFRAG_LENGTH)),
        password_(rtc::CreateRandomString(ICE_PWD_LENGTH)),
        role_conflict_(false),
        ports_destroyed_(0) {}

  ~PortTest() {
    // Workaround for tests that trigger async destruction of objects that we
    // need to give an opportunity here to run, before proceeding with other
    // teardown.
    rtc::Thread::Current()->ProcessMessages(0);
  }

 protected:
  std::string password() { return password_; }

  void TestLocalToLocal() {
    auto port1 = CreateUdpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateUdpPort(kLocalAddr2);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("udp", std::move(port1), "udp", std::move(port2), true,
                     truetruetrue);
  }
  void TestLocalToStun(NATType ntype) {
    auto port1 = CreateUdpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    nat_server2_ = CreateNatServer(kNatAddr2, ntype);
    auto port2 = CreateStunPort(kLocalAddr2, &nat_socket_factory2_);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("udp", std::move(port1), StunName(ntype), std::move(port2),
                     ntype == NAT_OPEN_CONE, true, ntype != NAT_SYMMETRIC,
                     true);
  }
  void TestLocalToRelay(ProtocolType proto) {
    auto port1 = CreateUdpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateRelayPort(kLocalAddr2, proto, PROTO_UDP);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("udp", std::move(port1), RelayName(proto),
                     std::move(port2), falsetruetruetrue);
  }
  void TestStunToLocal(NATType ntype) {
    nat_server1_ = CreateNatServer(kNatAddr1, ntype);
    auto port1 = CreateStunPort(kLocalAddr1, &nat_socket_factory1_);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateUdpPort(kLocalAddr2);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity(StunName(ntype), std::move(port1), "udp", std::move(port2),
                     true, ntype != NAT_SYMMETRIC, truetrue);
  }
  void TestStunToStun(NATType ntype1, NATType ntype2) {
    nat_server1_ = CreateNatServer(kNatAddr1, ntype1);
    auto port1 = CreateStunPort(kLocalAddr1, &nat_socket_factory1_);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    nat_server2_ = CreateNatServer(kNatAddr2, ntype2);
    auto port2 = CreateStunPort(kLocalAddr2, &nat_socket_factory2_);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity(StunName(ntype1), std::move(port1), StunName(ntype2),
                     std::move(port2), ntype2 == NAT_OPEN_CONE,
                     ntype1 != NAT_SYMMETRIC, ntype2 != NAT_SYMMETRIC,
                     ntype1 + ntype2 < (NAT_PORT_RESTRICTED + NAT_SYMMETRIC));
  }
  void TestStunToRelay(NATType ntype, ProtocolType proto) {
    nat_server1_ = CreateNatServer(kNatAddr1, ntype);
    auto port1 = CreateStunPort(kLocalAddr1, &nat_socket_factory1_);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateRelayPort(kLocalAddr2, proto, PROTO_UDP);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity(StunName(ntype), std::move(port1), RelayName(proto),
                     std::move(port2), false, ntype != NAT_SYMMETRIC, true,
                     true);
  }
  void TestTcpToTcp() {
    auto port1 = CreateTcpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateTcpPort(kLocalAddr2);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("tcp", std::move(port1), "tcp", std::move(port2), true,
                     falsetruetrue);
  }
  void TestTcpToRelay(ProtocolType proto) {
    auto port1 = CreateTcpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateRelayPort(kLocalAddr2, proto, PROTO_TCP);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("tcp", std::move(port1), RelayName(proto),
                     std::move(port2), falsefalsetruetrue);
  }
  void TestSslTcpToRelay(ProtocolType proto) {
    auto port1 = CreateTcpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateRelayPort(kLocalAddr2, proto, PROTO_SSLTCP);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
    TestConnectivity("ssltcp", std::move(port1), RelayName(proto),
                     std::move(port2), falsefalsetruetrue);
  }

  rtc::Network* MakeNetwork(const SocketAddress& addr) {
    networks_.emplace_back("unittest""unittest", addr.ipaddr(), 32);
    networks_.back().AddIP(addr.ipaddr());
    return &networks_.back();
  }

  rtc::Network* MakeNetworkMultipleAddrs(const SocketAddress& global_addr,
                                         const SocketAddress& link_local_addr) {
    networks_.emplace_back("unittest""unittest", global_addr.ipaddr(), 32,
                           rtc::ADAPTER_TYPE_UNKNOWN);
    networks_.back().AddIP(link_local_addr.ipaddr());
    networks_.back().AddIP(global_addr.ipaddr());
    networks_.back().AddIP(link_local_addr.ipaddr());
    return &networks_.back();
  }

  // helpers for above functions
  std::unique_ptr<UDPPort> CreateUdpPort(const SocketAddress& addr) {
    return CreateUdpPort(addr, &socket_factory_);
  }
  std::unique_ptr<UDPPort> CreateUdpPort(const SocketAddress& addr,
                                         PacketSocketFactory* socket_factory) {
    auto port = UDPPort::Create({.network_thread = &main_,
                                 .socket_factory = socket_factory,
                                 .network = MakeNetwork(addr),
                                 .ice_username_fragment = username_,
                                 .ice_password = password_,
                                 .field_trials = &field_trials_},
                                0, 0, true, std::nullopt);
    port->SetIceTiebreaker(kTiebreakerDefault);
    return port;
  }

  std::unique_ptr<UDPPort> CreateUdpPortMultipleAddrs(
      const SocketAddress& global_addr,
      const SocketAddress& link_local_addr,
      PacketSocketFactory* socket_factory) {
    auto port = UDPPort::Create(
        {.network_thread = &main_,
         .socket_factory = socket_factory,
         .network = MakeNetworkMultipleAddrs(global_addr, link_local_addr),
         .ice_username_fragment = username_,
         .ice_password = password_,
         .field_trials = &field_trials_},
        0, 0, true, std::nullopt);
    port->SetIceTiebreaker(kTiebreakerDefault);
    return port;
  }
  std::unique_ptr<TCPPort> CreateTcpPort(const SocketAddress& addr) {
    return CreateTcpPort(addr, &socket_factory_);
  }
  std::unique_ptr<TCPPort> CreateTcpPort(const SocketAddress& addr,
                                         PacketSocketFactory* socket_factory) {
    auto port = TCPPort::Create({.network_thread = &main_,
                                 .socket_factory = socket_factory,
                                 .network = MakeNetwork(addr),
                                 .ice_username_fragment = username_,
                                 .ice_password = password_,
                                 .field_trials = &field_trials_},
                                0, 0, true);
    port->SetIceTiebreaker(kTiebreakerDefault);
    return port;
  }
  std::unique_ptr<StunPort> CreateStunPort(
      const SocketAddress& addr,
      rtc::PacketSocketFactory* socket_factory) {
    ServerAddresses stun_servers;
    stun_servers.insert(kStunAddr);
    auto port = StunPort::Create({.network_thread = &main_,
                                  .socket_factory = socket_factory,
                                  .network = MakeNetwork(addr),
                                  .ice_username_fragment = username_,
                                  .ice_password = password_,
                                  .field_trials = &field_trials_},
                                 0, 0, stun_servers, std::nullopt);
    port->SetIceTiebreaker(kTiebreakerDefault);
    return port;
  }
  std::unique_ptr<Port> CreateRelayPort(const SocketAddress& addr,
                                        ProtocolType int_proto,
                                        ProtocolType ext_proto) {
    return CreateTurnPort(addr, &socket_factory_, int_proto, ext_proto);
  }
  std::unique_ptr<TurnPort> CreateTurnPort(const SocketAddress& addr,
                                           PacketSocketFactory* socket_factory,
                                           ProtocolType int_proto,
                                           ProtocolType ext_proto) {
    SocketAddress server_addr =
        int_proto == PROTO_TCP ? kTurnTcpIntAddr : kTurnUdpIntAddr;
    return CreateTurnPort(addr, socket_factory, int_proto, ext_proto,
                          server_addr);
  }
  std::unique_ptr<TurnPort> CreateTurnPort(
      const SocketAddress& addr,
      PacketSocketFactory* socket_factory,
      ProtocolType int_proto,
      ProtocolType ext_proto,
      const rtc::SocketAddress& server_addr) {
    RelayServerConfig config;
    config.credentials = kRelayCredentials;
    ProtocolAddress server_address(server_addr, int_proto);
    CreateRelayPortArgs args;
    args.network_thread = &main_;
    args.socket_factory = socket_factory;
    args.network = MakeNetwork(addr);
    args.username = username_;
    args.password = password_;
    args.server_address = &server_address;
    args.config = &config;
    args.field_trials = &field_trials_;

    auto port = TurnPort::Create(args, 0, 0);
    port->SetIceTiebreaker(kTiebreakerDefault);
    return port;
  }

  std::unique_ptr<rtc::NATServer> CreateNatServer(const SocketAddress& addr,
                                                  rtc::NATType type) {
    return std::make_unique<rtc::NATServer>(type, main_, ss_.get(), addr, addr,
                                            main_, ss_.get(), addr);
  }
  static const char* StunName(NATType type) {
    switch (type) {
      case NAT_OPEN_CONE:
        return "stun(open cone)";
      case NAT_ADDR_RESTRICTED:
        return "stun(addr restricted)";
      case NAT_PORT_RESTRICTED:
        return "stun(port restricted)";
      case NAT_SYMMETRIC:
        return "stun(symmetric)";
      default:
        return "stun(?)";
    }
  }
  static const char* RelayName(ProtocolType proto) {
    switch (proto) {
      case PROTO_UDP:
        return "turn(udp)";
      case PROTO_TCP:
        return "turn(tcp)";
      case PROTO_SSLTCP:
        return "turn(ssltcp)";
      case PROTO_TLS:
        return "turn(tls)";
      default:
        return "turn(?)";
    }
  }

  void TestCrossFamilyPorts(int type);

  void ExpectPortsCanConnect(bool can_connect, Port* p1, Port* p2);

  // This does all the work and then deletes `port1` and `port2`.
  void TestConnectivity(absl::string_view name1,
                        std::unique_ptr<Port> port1,
                        absl::string_view name2,
                        std::unique_ptr<Port> port2,
                        bool accept,
                        bool same_addr1,
                        bool same_addr2,
                        bool possible);

  // This connects the provided channels which have already started.  `ch1`
  // should have its Connection created (either through CreateConnection() or
  // TCP reconnecting mechanism before entering this function.
  void ConnectStartedChannels(TestChannel* ch1, TestChannel* ch2) {
    ASSERT_TRUE(ch1->conn());
    EXPECT_TRUE_WAIT(ch1->conn()->connected(),
                     kDefaultTimeout);  // for TCP connect
    ch1->Ping();
    WAIT(!ch2->remote_address().IsNil(), kShortTimeout);

    // Send a ping from dst to src.
    ch2->AcceptConnection(GetCandidate(ch1->port()));
    ch2->Ping();
    EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch2->conn()->write_state(),
                   kDefaultTimeout);
  }

  // This connects and disconnects the provided channels in the same sequence as
  // TestConnectivity with all options set to `true`.  It does not delete either
  // channel.
  void StartConnectAndStopChannels(TestChannel* ch1, TestChannel* ch2) {
    // Acquire addresses.
    ch1->Start();
    ch2->Start();

    ch1->CreateConnection(GetCandidate(ch2->port()));
    ConnectStartedChannels(ch1, ch2);

    // Destroy the connections.
    ch1->Stop();
    ch2->Stop();
  }

  // This disconnects both end's Connection and make sure ch2 ready for new
  // connection.
  void DisconnectTcpTestChannels(TestChannel* ch1, TestChannel* ch2) {
    TCPConnection* tcp_conn1 = static_cast<TCPConnection*>(ch1->conn());
    TCPConnection* tcp_conn2 = static_cast<TCPConnection*>(ch2->conn());
    ASSERT_TRUE(
        ss_->CloseTcpConnections(tcp_conn1->socket()->GetLocalAddress(),
                                 tcp_conn2->socket()->GetLocalAddress()));

    // Wait for both OnClose are delivered.
    EXPECT_TRUE_WAIT(!ch1->conn()->connected(), kDefaultTimeout);
    EXPECT_TRUE_WAIT(!ch2->conn()->connected(), kDefaultTimeout);

    // Ensure redundant SignalClose events on TcpConnection won't break tcp
    // reconnection. Chromium will fire SignalClose for all outstanding IPC
    // packets during reconnection.
    tcp_conn1->socket()->NotifyClosedForTest(0);
    tcp_conn2->socket()->NotifyClosedForTest(0);

    // Speed up destroying ch2's connection such that the test is ready to
    // accept a new connection from ch1 before ch1's connection destroys itself.
    ch2->Stop();
    EXPECT_TRUE_WAIT(ch2->conn() == NULL, kDefaultTimeout);
  }

  void TestTcpReconnect(bool ping_after_disconnected,
                        bool send_after_disconnected) {
    auto port1 = CreateTcpPort(kLocalAddr1);
    port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
    auto port2 = CreateTcpPort(kLocalAddr2);
    port2->SetIceRole(cricket::ICEROLE_CONTROLLED);

    port1->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
    port2->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);

    // Set up channels and ensure both ports will be deleted.
    TestChannel ch1(std::move(port1));
    TestChannel ch2(std::move(port2));
    EXPECT_EQ(0, ch1.complete_count());
    EXPECT_EQ(0, ch2.complete_count());

    ch1.Start();
    ch2.Start();
    ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
    ASSERT_EQ_WAIT(1, ch2.complete_count(), kDefaultTimeout);

    // Initial connecting the channel, create connection on channel1.
    ch1.CreateConnection(GetCandidate(ch2.port()));
    ConnectStartedChannels(&ch1, &ch2);

    // Shorten the timeout period.
    const int kTcpReconnectTimeout = kDefaultTimeout;
    static_cast<TCPConnection*>(ch1.conn())
        ->set_reconnection_timeout(kTcpReconnectTimeout);
    static_cast<TCPConnection*>(ch2.conn())
        ->set_reconnection_timeout(kTcpReconnectTimeout);

    EXPECT_FALSE(ch1.connection_ready_to_send());
    EXPECT_FALSE(ch2.connection_ready_to_send());

    // Once connected, disconnect them.
    DisconnectTcpTestChannels(&ch1, &ch2);

    if (send_after_disconnected || ping_after_disconnected) {
      if (send_after_disconnected) {
        // First SendData after disconnect should fail but will trigger
        // reconnect.
        EXPECT_EQ(-1, ch1.SendData(data, static_cast<int>(strlen(data))));
      }

      if (ping_after_disconnected) {
        // Ping should trigger reconnect.
        ch1.Ping();
      }

      // Wait for channel's outgoing TCPConnection connected.
      EXPECT_TRUE_WAIT(ch1.conn()->connected(), kDefaultTimeout);

      // Verify that we could still connect channels.
      ConnectStartedChannels(&ch1, &ch2);
      EXPECT_TRUE_WAIT(ch1.connection_ready_to_send(), kTcpReconnectTimeout);
      // Channel2 is the passive one so a new connection is created during
      // reconnect. This new connection should never have issued ENOTCONN
      // hence the connection_ready_to_send() should be false.
      EXPECT_FALSE(ch2.connection_ready_to_send());
    } else {
      EXPECT_EQ(ch1.conn()->write_state(), Connection::STATE_WRITABLE);
      // Since the reconnection never happens, the connections should have been
      // destroyed after the timeout.
      EXPECT_TRUE_WAIT(!ch1.conn(), kTcpReconnectTimeout + kDefaultTimeout);
      EXPECT_TRUE(!ch2.conn());
    }

    // Tear down and ensure that goes smoothly.
    ch1.Stop();
    ch2.Stop();
    EXPECT_TRUE_WAIT(ch1.conn() == NULL, kDefaultTimeout);
    EXPECT_TRUE_WAIT(ch2.conn() == NULL, kDefaultTimeout);
  }

  std::unique_ptr<IceMessage> CreateStunMessage(StunMessageType type) {
    auto msg = std::make_unique<IceMessage>(type, "TESTTESTTEST");
    return msg;
  }
  std::unique_ptr<IceMessage> CreateStunMessageWithUsername(
      StunMessageType type,
      absl::string_view username) {
    std::unique_ptr<IceMessage> msg = CreateStunMessage(type);
    msg->AddAttribute(std::make_unique<StunByteStringAttribute>(
        STUN_ATTR_USERNAME, std::string(username)));
    return msg;
  }
  std::unique_ptr<TestPort> CreateTestPort(
      const rtc::SocketAddress& addr,
      absl::string_view username,
      absl::string_view password,
      const webrtc::FieldTrialsView* field_trials = nullptr) {
    Port::PortParametersRef args = {.network_thread = &main_,
                                    .socket_factory = &socket_factory_,
                                    .network = MakeNetwork(addr),
                                    .ice_username_fragment = username,
                                    .ice_password = password,
                                    .field_trials = field_trials};
    auto port = std::make_unique<TestPort>(args, 0, 0);
    port->SignalRoleConflict.connect(this, &PortTest::OnRoleConflict);
    return port;
  }
  std::unique_ptr<TestPort> CreateTestPort(const rtc::SocketAddress& addr,
                                           absl::string_view username,
                                           absl::string_view password,
                                           cricket::IceRole role,
                                           int tiebreaker) {
    auto port = CreateTestPort(addr, username, password);
    port->SetIceRole(role);
    port->SetIceTiebreaker(tiebreaker);
    return port;
  }
  // Overload to create a test port given an rtc::Network directly.
  std::unique_ptr<TestPort> CreateTestPort(const rtc::Network* network,
                                           absl::string_view username,
                                           absl::string_view password) {
    Port::PortParametersRef args = {.network_thread = &main_,
                                    .socket_factory = &socket_factory_,
                                    .network = network,
                                    .ice_username_fragment = username,
                                    .ice_password = password,
                                    .field_trials = nullptr};
    auto port = std::make_unique<TestPort>(args, 0, 0);
    port->SignalRoleConflict.connect(this, &PortTest::OnRoleConflict);
    return port;
  }

  void OnRoleConflict(PortInterface* port) { role_conflict_ = true; }
  bool role_conflict() const { return role_conflict_; }

  void ConnectToSignalDestroyed(PortInterface* port) {
    port->SubscribePortDestroyed(
        [this](PortInterface* port) { OnDestroyed(port); });
  }

  void OnDestroyed(PortInterface* port) { ++ports_destroyed_; }
  int ports_destroyed() const { return ports_destroyed_; }

  rtc::BasicPacketSocketFactory* nat_socket_factory1() {
    return &nat_socket_factory1_;
  }

  rtc::VirtualSocketServer* vss() { return ss_.get(); }

 private:
  // When a "create port" helper method is called with an IP, we create a
  // Network with that IP and add it to this list. Using a list instead of a
  // vector so that when it grows, pointers aren't invalidated.
  std::list<rtc::Network> networks_;
  std::unique_ptr<rtc::VirtualSocketServer> ss_;
  rtc::AutoSocketServerThread main_;
  rtc::BasicPacketSocketFactory socket_factory_;
  std::unique_ptr<rtc::NATServer> nat_server1_;
  std::unique_ptr<rtc::NATServer> nat_server2_;
  rtc::NATSocketFactory nat_factory1_;
  rtc::NATSocketFactory nat_factory2_;
  rtc::BasicPacketSocketFactory nat_socket_factory1_;
  rtc::BasicPacketSocketFactory nat_socket_factory2_;
  TestStunServer::StunServerPtr stun_server_;
  TestTurnServer turn_server_;
  std::string username_;
  std::string password_;
  bool role_conflict_;
  int ports_destroyed_;
  webrtc::test::ScopedKeyValueConfig field_trials_;
};

void PortTest::TestConnectivity(absl::string_view name1,
                                std::unique_ptr<Port> port1,
                                absl::string_view name2,
                                std::unique_ptr<Port> port2,
                                bool accept,
                                bool same_addr1,
                                bool same_addr2,
                                bool possible) {
  rtc::ScopedFakeClock clock;
  RTC_LOG(LS_INFO) << "Test: " << name1 << " to " << name2 << ": ";
  port1->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
  port2->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);

  // Set up channels and ensure both ports will be deleted.
  TestChannel ch1(std::move(port1));
  TestChannel ch2(std::move(port2));
  EXPECT_EQ(0, ch1.complete_count());
  EXPECT_EQ(0, ch2.complete_count());

  // Acquire addresses.
  ch1.Start();
  ch2.Start();
  ASSERT_EQ_SIMULATED_WAIT(1, ch1.complete_count(), kDefaultTimeout, clock);
  ASSERT_EQ_SIMULATED_WAIT(1, ch2.complete_count(), kDefaultTimeout, clock);

  // Send a ping from src to dst. This may or may not make it.
  ch1.CreateConnection(GetCandidate(ch2.port()));
  ASSERT_TRUE(ch1.conn() != NULL);
  EXPECT_TRUE_SIMULATED_WAIT(ch1.conn()->connected(), kDefaultTimeout,
                             clock);  // for TCP connect
  ch1.Ping();
  SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock);

  if (accept) {
    // We are able to send a ping from src to dst. This is the case when
    // sending to UDP ports and cone NATs.
    EXPECT_TRUE(ch1.remote_address().IsNil());
    EXPECT_EQ(ch2.remote_fragment(), ch1.port()->username_fragment());

    // Ensure the ping came from the same address used for src.
    // This is the case unless the source NAT was symmetric.
    if (same_addr1)
      EXPECT_EQ(ch2.remote_address(), GetAddress(ch1.port()));
    EXPECT_TRUE(same_addr2);

    // Send a ping from dst to src.
    ch2.AcceptConnection(GetCandidate(ch1.port()));
    ASSERT_TRUE(ch2.conn() != NULL);
    ch2.Ping();
    EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE,
                             ch2.conn()->write_state(), kDefaultTimeout, clock);
  } else {
    // We can't send a ping from src to dst, so flip it around. This will happen
    // when the destination NAT is addr/port restricted or symmetric.
    EXPECT_TRUE(ch1.remote_address().IsNil());
    EXPECT_TRUE(ch2.remote_address().IsNil());

    // Send a ping from dst to src. Again, this may or may not make it.
    ch2.CreateConnection(GetCandidate(ch1.port()));
    ASSERT_TRUE(ch2.conn() != NULL);
    ch2.Ping();
    SIMULATED_WAIT(ch2.conn()->write_state() == Connection::STATE_WRITABLE,
                   kShortTimeout, clock);

    if (same_addr1 && same_addr2) {
      // The new ping got back to the source.
      EXPECT_TRUE(ch1.conn()->receiving());
      EXPECT_EQ(Connection::STATE_WRITABLE, ch2.conn()->write_state());

      // First connection may not be writable if the first ping did not get
      // through.  So we will have to do another.
      if (ch1.conn()->write_state() == Connection::STATE_WRITE_INIT) {
        ch1.Ping();
        EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE,
                                 ch1.conn()->write_state(), kDefaultTimeout,
                                 clock);
      }
    } else if (!same_addr1 && possible) {
      // The new ping went to the candidate address, but that address was bad.
      // This will happen when the source NAT is symmetric.
      EXPECT_TRUE(ch1.remote_address().IsNil());
      EXPECT_TRUE(ch2.remote_address().IsNil());

      // However, since we have now sent a ping to the source IP, we should be
      // able to get a ping from it. This gives us the real source address.
      ch1.Ping();
      EXPECT_TRUE_SIMULATED_WAIT(!ch2.remote_address().IsNil(), kDefaultTimeout,
                                 clock);
      EXPECT_FALSE(ch2.conn()->receiving());
      EXPECT_TRUE(ch1.remote_address().IsNil());

      // Pick up the actual address and establish the connection.
      ch2.AcceptConnection(GetCandidate(ch1.port()));
      ASSERT_TRUE(ch2.conn() != NULL);
      ch2.Ping();
      EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE,
                               ch2.conn()->write_state(), kDefaultTimeout,
                               clock);
    } else if (!same_addr2 && possible) {
      // The new ping came in, but from an unexpected address. This will happen
      // when the destination NAT is symmetric.
      EXPECT_FALSE(ch1.remote_address().IsNil());
      EXPECT_FALSE(ch1.conn()->receiving());

      // Update our address and complete the connection.
      ch1.AcceptConnection(GetCandidate(ch2.port()));
      ch1.Ping();
      EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE,
                               ch1.conn()->write_state(), kDefaultTimeout,
                               clock);
    } else {  // (!possible)
      // There should be s no way for the pings to reach each other. Check it.
      EXPECT_TRUE(ch1.remote_address().IsNil());
      EXPECT_TRUE(ch2.remote_address().IsNil());
      ch1.Ping();
      SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock);
      EXPECT_TRUE(ch1.remote_address().IsNil());
      EXPECT_TRUE(ch2.remote_address().IsNil());
    }
  }

  // Everything should be good, unless we know the situation is impossible.
  ASSERT_TRUE(ch1.conn() != NULL);
  ASSERT_TRUE(ch2.conn() != NULL);
  if (possible) {
    EXPECT_TRUE(ch1.conn()->receiving());
    EXPECT_EQ(Connection::STATE_WRITABLE, ch1.conn()->write_state());
    EXPECT_TRUE(ch2.conn()->receiving());
    EXPECT_EQ(Connection::STATE_WRITABLE, ch2.conn()->write_state());
  } else {
    EXPECT_FALSE(ch1.conn()->receiving());
    EXPECT_NE(Connection::STATE_WRITABLE, ch1.conn()->write_state());
    EXPECT_FALSE(ch2.conn()->receiving());
    EXPECT_NE(Connection::STATE_WRITABLE, ch2.conn()->write_state());
  }

  // Tear down and ensure that goes smoothly.
  ch1.Stop();
  ch2.Stop();
  EXPECT_TRUE_SIMULATED_WAIT(ch1.conn() == NULL, kDefaultTimeout, clock);
  EXPECT_TRUE_SIMULATED_WAIT(ch2.conn() == NULL, kDefaultTimeout, clock);
}

class FakePacketSocketFactory : public rtc::PacketSocketFactory {
 public:
  FakePacketSocketFactory()
      : next_udp_socket_(NULL), next_server_tcp_socket_(NULL) {}
  ~FakePacketSocketFactory() override {}

  AsyncPacketSocket* CreateUdpSocket(const SocketAddress& address,
                                     uint16_t min_port,
                                     uint16_t max_port) override {
    EXPECT_TRUE(next_udp_socket_ != NULL);
    AsyncPacketSocket* result = next_udp_socket_;
    next_udp_socket_ = NULL;
    return result;
  }

  AsyncListenSocket* CreateServerTcpSocket(const SocketAddress& local_address,
                                           uint16_t min_port,
                                           uint16_t max_port,
                                           int opts) override {
    EXPECT_TRUE(next_server_tcp_socket_ != NULL);
    AsyncListenSocket* result = next_server_tcp_socket_;
    next_server_tcp_socket_ = NULL;
    return result;
  }

  AsyncPacketSocket* CreateClientTcpSocket(
      const SocketAddress& local_address,
      const SocketAddress& remote_address,
      const rtc::PacketSocketTcpOptions& opts) override {
    EXPECT_TRUE(next_client_tcp_socket_.has_value());
    AsyncPacketSocket* result = *next_client_tcp_socket_;
    next_client_tcp_socket_ = nullptr;
    return result;
  }

  void set_next_udp_socket(AsyncPacketSocket* next_udp_socket) {
    next_udp_socket_ = next_udp_socket;
  }
  void set_next_server_tcp_socket(AsyncListenSocket* next_server_tcp_socket) {
    next_server_tcp_socket_ = next_server_tcp_socket;
  }
  void set_next_client_tcp_socket(AsyncPacketSocket* next_client_tcp_socket) {
    next_client_tcp_socket_ = next_client_tcp_socket;
  }
  std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
      override {
    return nullptr;
  }

 private:
  AsyncPacketSocket* next_udp_socket_;
  AsyncListenSocket* next_server_tcp_socket_;
  std::optional<AsyncPacketSocket*> next_client_tcp_socket_;
};

class FakeAsyncPacketSocket : public AsyncPacketSocket {
 public:
  // Returns current local address. Address may be set to NULL if the
  // socket is not bound yet (GetState() returns STATE_BINDING).
  virtual SocketAddress GetLocalAddress() const { return local_address_; }

  // Returns remote address. Returns zeroes if this is not a client TCP socket.
  virtual SocketAddress GetRemoteAddress() const { return remote_address_; }

  // Send a packet.
  virtual int Send(const void* pv,
                   size_t cb,
                   const rtc::PacketOptions& options) {
    if (error_ == 0) {
      return static_cast<int>(cb);
    } else {
      return -1;
    }
  }
  virtual int SendTo(const void* pv,
                     size_t cb,
                     const SocketAddress& addr,
                     const rtc::PacketOptions& options) {
    if (error_ == 0) {
      return static_cast<int>(cb);
    } else {
      return -1;
    }
  }
  virtual int Close() { return 0; }

  virtual State GetState() const { return state_; }
  virtual int GetOption(Socket::Option opt, int* value) { return 0; }
  virtual int SetOption(Socket::Option opt, int value) { return 0; }
  virtual int GetError() const { return 0; }
  virtual void SetError(int error) { error_ = error; }

  void set_state(State state) { state_ = state; }

  SocketAddress local_address_;
  SocketAddress remote_address_;

 private:
  int error_ = 0;
  State state_;
};

class FakeAsyncListenSocket : public AsyncListenSocket {
 public:
  // Returns current local address. Address may be set to NULL if the
  // socket is not bound yet (GetState() returns STATE_BINDING).
  virtual SocketAddress GetLocalAddress() const { return local_address_; }
  void Bind(const SocketAddress& address) {
    local_address_ = address;
    state_ = State::kBound;
  }
  virtual int GetOption(Socket::Option opt, int* value) { return 0; }
  virtual int SetOption(Socket::Option opt, int value) { return 0; }
  virtual State GetState() const { return state_; }

 private:
  SocketAddress local_address_;
  State state_ = State::kClosed;
};

// Local -> XXXX
TEST_F(PortTest, TestLocalToLocal) {
  TestLocalToLocal();
}

TEST_F(PortTest, TestLocalToConeNat) {
  TestLocalToStun(NAT_OPEN_CONE);
}

TEST_F(PortTest, TestLocalToARNat) {
  TestLocalToStun(NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestLocalToPRNat) {
  TestLocalToStun(NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestLocalToSymNat) {
  TestLocalToStun(NAT_SYMMETRIC);
}

// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3316.
TEST_F(PortTest, DISABLED_TestLocalToTurn) {
  TestLocalToRelay(PROTO_UDP);
}

// Cone NAT -> XXXX
TEST_F(PortTest, TestConeNatToLocal) {
  TestStunToLocal(NAT_OPEN_CONE);
}

TEST_F(PortTest, TestConeNatToConeNat) {
  TestStunToStun(NAT_OPEN_CONE, NAT_OPEN_CONE);
}

TEST_F(PortTest, TestConeNatToARNat) {
  TestStunToStun(NAT_OPEN_CONE, NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestConeNatToPRNat) {
  TestStunToStun(NAT_OPEN_CONE, NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestConeNatToSymNat) {
  TestStunToStun(NAT_OPEN_CONE, NAT_SYMMETRIC);
}

TEST_F(PortTest, TestConeNatToTurn) {
  TestStunToRelay(NAT_OPEN_CONE, PROTO_UDP);
}

// Address-restricted NAT -> XXXX
TEST_F(PortTest, TestARNatToLocal) {
  TestStunToLocal(NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestARNatToConeNat) {
  TestStunToStun(NAT_ADDR_RESTRICTED, NAT_OPEN_CONE);
}

TEST_F(PortTest, TestARNatToARNat) {
  TestStunToStun(NAT_ADDR_RESTRICTED, NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestARNatToPRNat) {
  TestStunToStun(NAT_ADDR_RESTRICTED, NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestARNatToSymNat) {
  TestStunToStun(NAT_ADDR_RESTRICTED, NAT_SYMMETRIC);
}

TEST_F(PortTest, TestARNatToTurn) {
  TestStunToRelay(NAT_ADDR_RESTRICTED, PROTO_UDP);
}

// Port-restricted NAT -> XXXX
TEST_F(PortTest, TestPRNatToLocal) {
  TestStunToLocal(NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestPRNatToConeNat) {
  TestStunToStun(NAT_PORT_RESTRICTED, NAT_OPEN_CONE);
}

TEST_F(PortTest, TestPRNatToARNat) {
  TestStunToStun(NAT_PORT_RESTRICTED, NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestPRNatToPRNat) {
  TestStunToStun(NAT_PORT_RESTRICTED, NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestPRNatToSymNat) {
  // Will "fail"
  TestStunToStun(NAT_PORT_RESTRICTED, NAT_SYMMETRIC);
}

TEST_F(PortTest, TestPRNatToTurn) {
  TestStunToRelay(NAT_PORT_RESTRICTED, PROTO_UDP);
}

// Symmetric NAT -> XXXX
TEST_F(PortTest, TestSymNatToLocal) {
  TestStunToLocal(NAT_SYMMETRIC);
}

TEST_F(PortTest, TestSymNatToConeNat) {
  TestStunToStun(NAT_SYMMETRIC, NAT_OPEN_CONE);
}

TEST_F(PortTest, TestSymNatToARNat) {
  TestStunToStun(NAT_SYMMETRIC, NAT_ADDR_RESTRICTED);
}

TEST_F(PortTest, TestSymNatToPRNat) {
  // Will "fail"
  TestStunToStun(NAT_SYMMETRIC, NAT_PORT_RESTRICTED);
}

TEST_F(PortTest, TestSymNatToSymNat) {
  // Will "fail"
  TestStunToStun(NAT_SYMMETRIC, NAT_SYMMETRIC);
}

TEST_F(PortTest, TestSymNatToTurn) {
  TestStunToRelay(NAT_SYMMETRIC, PROTO_UDP);
}

// Outbound TCP -> XXXX
TEST_F(PortTest, TestTcpToTcp) {
  TestTcpToTcp();
}

TEST_F(PortTest, TestTcpReconnectOnSendPacket) {
  TestTcpReconnect(false /* ping */, true /* send */);
}

TEST_F(PortTest, TestTcpReconnectOnPing) {
  TestTcpReconnect(true /* ping */, false /* send */);
}

TEST_F(PortTest, TestTcpReconnectTimeout) {
  TestTcpReconnect(false /* ping */, false /* send */);
}

// Test when TcpConnection never connects, the OnClose() will be called to
// destroy the connection.
TEST_F(PortTest, TestTcpNeverConnect) {
  auto port1 = CreateTcpPort(kLocalAddr1);
  port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
  port1->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);

  // Set up a channel and ensure the port will be deleted.
  TestChannel ch1(std::move(port1));
  EXPECT_EQ(0, ch1.complete_count());

  ch1.Start();
  ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);

  std::unique_ptr<rtc::Socket> server(
      vss()->CreateSocket(kLocalAddr2.family(), SOCK_STREAM));
  // Bind but not listen.
  EXPECT_EQ(0, server->Bind(kLocalAddr2));

  Candidate c = GetCandidate(ch1.port());
  c.set_address(server->GetLocalAddress());

  ch1.CreateConnection(c);
  EXPECT_TRUE(ch1.conn());
  EXPECT_TRUE_WAIT(!ch1.conn(), kDefaultTimeout);  // for TCP connect
}

/* TODO(?): Enable these once testrelayserver can accept external TCP.
TEST_F(PortTest, TestTcpToTcpRelay) {
  TestTcpToRelay(PROTO_TCP);
}

TEST_F(PortTest, TestTcpToSslTcpRelay) {
  TestTcpToRelay(PROTO_SSLTCP);
}
*/


// Outbound SSLTCP -> XXXX
/* TODO(?): Enable these once testrelayserver can accept external SSL.
TEST_F(PortTest, TestSslTcpToTcpRelay) {
  TestSslTcpToRelay(PROTO_TCP);
}

TEST_F(PortTest, TestSslTcpToSslTcpRelay) {
  TestSslTcpToRelay(PROTO_SSLTCP);
}
*/


// Test that a connection will be dead and deleted if
// i) it has never received anything for MIN_CONNECTION_LIFETIME milliseconds
//    since it was created, or
// ii) it has not received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT
//     milliseconds since last receiving.
TEST_F(PortTest, TestConnectionDead) {
  TestChannel ch1(CreateUdpPort(kLocalAddr1));
  TestChannel ch2(CreateUdpPort(kLocalAddr2));
  // Acquire address.
  ch1.Start();
  ch2.Start();
  ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
  ASSERT_EQ_WAIT(1, ch2.complete_count(), kDefaultTimeout);

  // Test case that the connection has never received anything.
  int64_t before_created = rtc::TimeMillis();
  ch1.CreateConnection(GetCandidate(ch2.port()));
  int64_t after_created = rtc::TimeMillis();
  Connection* conn = ch1.conn();
  ASSERT_NE(conn, nullptr);
  // It is not dead if it is after MIN_CONNECTION_LIFETIME but not pruned.
  conn->UpdateState(after_created + MIN_CONNECTION_LIFETIME + 1);
  rtc::Thread::Current()->ProcessMessages(0);
  EXPECT_TRUE(ch1.conn() != nullptr);
  // It is not dead if it is before MIN_CONNECTION_LIFETIME and pruned.
  conn->UpdateState(before_created + MIN_CONNECTION_LIFETIME - 1);
  conn->Prune();
  rtc::Thread::Current()->ProcessMessages(0);
  EXPECT_TRUE(ch1.conn() != nullptr);
  // It will be dead after MIN_CONNECTION_LIFETIME and pruned.
  conn->UpdateState(after_created + MIN_CONNECTION_LIFETIME + 1);
  EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kDefaultTimeout);

  // Test case that the connection has received something.
  // Create a connection again and receive a ping.
  ch1.CreateConnection(GetCandidate(ch2.port()));
  conn = ch1.conn();
  ASSERT_NE(conn, nullptr);
  int64_t before_last_receiving = rtc::TimeMillis();
  conn->ReceivedPing();
  int64_t after_last_receiving = rtc::TimeMillis();
  // The connection will be dead after DEAD_CONNECTION_RECEIVE_TIMEOUT
  conn->UpdateState(before_last_receiving + DEAD_CONNECTION_RECEIVE_TIMEOUT -
                    1);
  rtc::Thread::Current()->ProcessMessages(100);
  EXPECT_TRUE(ch1.conn() != nullptr);
  conn->UpdateState(after_last_receiving + DEAD_CONNECTION_RECEIVE_TIMEOUT + 1);
  EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kDefaultTimeout);
}

TEST_F(PortTest, TestConnectionDeadWithDeadConnectionTimeout) {
  TestChannel ch1(CreateUdpPort(kLocalAddr1));
  TestChannel ch2(CreateUdpPort(kLocalAddr2));
  // Acquire address.
  ch1.Start();
  ch2.Start();
  ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
  ASSERT_EQ_WAIT(1, ch2.complete_count(), kDefaultTimeout);

  // Note: set field trials manually since they are parsed by
  // P2PTransportChannel but P2PTransportChannel is not used in this test.
  IceFieldTrials field_trials;
  field_trials.dead_connection_timeout_ms = 90000;

  // Create a connection again and receive a ping.
  ch1.CreateConnection(GetCandidate(ch2.port()));
  auto conn = ch1.conn();
  conn->SetIceFieldTrials(&field_trials);

  ASSERT_NE(conn, nullptr);
  int64_t before_last_receiving = rtc::TimeMillis();
  conn->ReceivedPing();
  int64_t after_last_receiving = rtc::TimeMillis();
  // The connection will be dead after 90s
  conn->UpdateState(before_last_receiving + 90000 - 1);
  rtc::Thread::Current()->ProcessMessages(100);
  EXPECT_TRUE(ch1.conn() != nullptr);
  conn->UpdateState(after_last_receiving + 90000 + 1);
  EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kDefaultTimeout);
}

TEST_F(PortTest, TestConnectionDeadOutstandingPing) {
  auto port1 = CreateUdpPort(kLocalAddr1);
  port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
  port1->SetIceTiebreaker(kTiebreaker1);
  auto port2 = CreateUdpPort(kLocalAddr2);
  port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
  port2->SetIceTiebreaker(kTiebreaker2);

  TestChannel ch1(std::move(port1));
  TestChannel ch2(std::move(port2));
  // Acquire address.
  ch1.Start();
  ch2.Start();
  ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
  ASSERT_EQ_WAIT(1, ch2.complete_count(), kDefaultTimeout);

  // Note: set field trials manually since they are parsed by
  // P2PTransportChannel but P2PTransportChannel is not used in this test.
  IceFieldTrials field_trials;
  field_trials.dead_connection_timeout_ms = 360000;

  // Create a connection again and receive a ping and then send
  // a ping and keep it outstanding.
  ch1.CreateConnection(GetCandidate(ch2.port()));
  auto conn = ch1.conn();
  conn->SetIceFieldTrials(&field_trials);

  ASSERT_NE(conn, nullptr);
  conn->ReceivedPing();
  int64_t send_ping_timestamp = rtc::TimeMillis();
  conn->Ping(send_ping_timestamp);

  // The connection will be dead 30s after the ping was sent.
  conn->UpdateState(send_ping_timestamp + DEAD_CONNECTION_RECEIVE_TIMEOUT - 1);
  rtc::Thread::Current()->ProcessMessages(100);
  EXPECT_TRUE(ch1.conn() != nullptr);
  conn->UpdateState(send_ping_timestamp + DEAD_CONNECTION_RECEIVE_TIMEOUT + 1);
  EXPECT_TRUE_WAIT(ch1.conn() == nullptr, kDefaultTimeout);
}

// This test case verifies standard ICE features in STUN messages. Currently it
// verifies Message Integrity attribute in STUN messages and username in STUN
// binding request will have colon (":") between remote and local username.
TEST_F(PortTest, TestLocalToLocalStandard) {
  auto port1 = CreateUdpPort(kLocalAddr1);
  port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
  port1->SetIceTiebreaker(kTiebreaker1);
  auto port2 = CreateUdpPort(kLocalAddr2);
  port2->SetIceRole(cricket::ICEROLE_CONTROLLED);
  port2->SetIceTiebreaker(kTiebreaker2);
  // Same parameters as TestLocalToLocal above.
  TestConnectivity("udp", std::move(port1), "udp", std::move(port2), truetrue,
                   truetrue);
}

// This test is trying to validate a successful and failure scenario in a
// loopback test when protocol is RFC5245. For success IceTiebreaker, username
// should remain equal to the request generated by the port and role of port
// must be in controlling.
TEST_F(PortTest, TestLoopbackCall) {
  auto lport = CreateTestPort(kLocalAddr1, "lfrag""lpass");
  lport->SetIceRole(cricket::ICEROLE_CONTROLLING);
  lport->SetIceTiebreaker(kTiebreaker1);
  lport->PrepareAddress();
  ASSERT_FALSE(lport->Candidates().empty());
  Connection* conn =
      lport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE);
  conn->Ping(0);

  ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
  IceMessage* msg = lport->last_stun_msg();
  EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
  conn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
                                         rtc::SocketAddress(), std::nullopt));
  ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
  msg = lport->last_stun_msg();
  EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type());

  // If the tiebreaker value is different from port, we expect a error
  // response.
  lport->Reset();
  lport->AddCandidateAddress(kLocalAddr2);
  // Creating a different connection as `conn` is receiving.
  Connection* conn1 =
      lport->CreateConnection(lport->Candidates()[1], Port::ORIGIN_MESSAGE);
  conn1->Ping(0);

  ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
  msg = lport->last_stun_msg();
  EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
  std::unique_ptr<IceMessage> modified_req(
      CreateStunMessage(STUN_BINDING_REQUEST));
  const StunByteStringAttribute* username_attr =
      msg->GetByteString(STUN_ATTR_USERNAME);
  modified_req->AddAttribute(std::make_unique<StunByteStringAttribute>(
      STUN_ATTR_USERNAME, username_attr->string_view()));
  // To make sure we receive error response, adding tiebreaker less than
  // what's present in request.
  modified_req->AddAttribute(std::make_unique<StunUInt64Attribute>(
      STUN_ATTR_ICE_CONTROLLING, kTiebreaker1 - 1));
  modified_req->AddMessageIntegrity("lpass");
  modified_req->AddFingerprint();

  lport->Reset();
  auto buf = std::make_unique<ByteBufferWriter>();
  WriteStunMessage(*modified_req, buf.get());
  conn1->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
      reinterpret_cast<const char*>(buf->Data()), buf->Length(),
      /*packet_time_us=*/-1));
  ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
  msg = lport->last_stun_msg();
  EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, msg->type());
}

// This test verifies role conflict signal is received when there is
// conflict in the role. In this case both ports are in controlling and
// `rport` has higher tiebreaker value than `lport`. Since `lport` has lower
// value of tiebreaker, when it receives ping request from `rport` it will
// send role conflict signal.
TEST_F(PortTest, TestIceRoleConflict) {
  auto lport = CreateTestPort(kLocalAddr1, "lfrag""lpass");
  lport->SetIceRole(cricket::ICEROLE_CONTROLLING);
  lport->SetIceTiebreaker(kTiebreaker1);
  auto rport = CreateTestPort(kLocalAddr2, "rfrag""rpass");
  rport->SetIceRole(cricket::ICEROLE_CONTROLLING);
  rport->SetIceTiebreaker(kTiebreaker2);

  lport->PrepareAddress();
  rport->PrepareAddress();
  ASSERT_FALSE(lport->Candidates().empty());
  ASSERT_FALSE(rport->Candidates().empty());
  Connection* lconn =
      lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE);
  Connection* rconn =
      rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE);
  rconn->Ping(0);

  ASSERT_TRUE_WAIT(rport->last_stun_msg() != NULL, kDefaultTimeout);
  IceMessage* msg = rport->last_stun_msg();
  EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
  // Send rport binding request to lport.
  lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
                                          rtc::SocketAddress(), std::nullopt));

  ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
  EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type());
  EXPECT_TRUE(role_conflict());
}

TEST_F(PortTest, TestTcpNoDelay) {
  rtc::ScopedFakeClock clock;
  auto port1 = CreateTcpPort(kLocalAddr1);
  port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
  int option_value = -1;
  int success = port1->GetOption(rtc::Socket::OPT_NODELAY, &option_value);
  ASSERT_EQ(0, success);  // GetOption() should complete successfully w/ 0
  EXPECT_EQ(1, option_value);

  auto port2 = CreateTcpPort(kLocalAddr2);
  port2->SetIceRole(cricket::ICEROLE_CONTROLLED);

  // Set up a connection, and verify that option is set on connected sockets at
  // both ends.
  TestChannel ch1(std::move(port1));
  TestChannel ch2(std::move(port2));
  // Acquire addresses.
  ch1.Start();
  ch2.Start();
  ASSERT_EQ_SIMULATED_WAIT(1, ch1.complete_count(), kDefaultTimeout, clock);
  ASSERT_EQ_SIMULATED_WAIT(1, ch2.complete_count(), kDefaultTimeout, clock);
  // Connect and send a ping from src to dst.
  ch1.CreateConnection(GetCandidate(ch2.port()));
  ASSERT_TRUE(ch1.conn() != NULL);
  EXPECT_TRUE_SIMULATED_WAIT(ch1.conn()->connected(), kDefaultTimeout,
                             clock);  // for TCP connect
  ch1.Ping();
  SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock);

  // Accept the connection.
  ch2.AcceptConnection(GetCandidate(ch1.port()));
  ASSERT_TRUE(ch2.conn() != NULL);

  option_value = -1;
  success = static_cast<TCPConnection*>(ch1.conn())
                ->socket()
                ->GetOption(rtc::Socket::OPT_NODELAY, &option_value);
  ASSERT_EQ(0, success);
  EXPECT_EQ(1, option_value);

  option_value = -1;
  success = static_cast<TCPConnection*>(ch2.conn())
                ->socket()
                ->GetOption(rtc::Socket::OPT_NODELAY, &option_value);
  ASSERT_EQ(0, success);
  EXPECT_EQ(1, option_value);
}

TEST_F(PortTest, TestDelayedBindingUdp) {
  FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket();
  FakePacketSocketFactory socket_factory;

  socket_factory.set_next_udp_socket(socket);
  auto port = CreateUdpPort(kLocalAddr1, &socket_factory);

  socket->set_state(AsyncPacketSocket::STATE_BINDING);
  port->PrepareAddress();

  EXPECT_EQ(0U, port->Candidates().size());
  socket->SignalAddressReady(socket, kLocalAddr2);

  EXPECT_EQ(1U, port->Candidates().size());
}

TEST_F(PortTest, TestDisableInterfaceOfTcpPort) {
  FakeAsyncListenSocket* lsocket = new FakeAsyncListenSocket();
  FakeAsyncListenSocket* rsocket = new FakeAsyncListenSocket();
  FakePacketSocketFactory socket_factory;

  socket_factory.set_next_server_tcp_socket(lsocket);
  auto lport = CreateTcpPort(kLocalAddr1, &socket_factory);

  socket_factory.set_next_server_tcp_socket(rsocket);
  auto rport = CreateTcpPort(kLocalAddr2, &socket_factory);

  lsocket->Bind(kLocalAddr1);
  rsocket->Bind(kLocalAddr2);

  lport->SetIceRole(cricket::ICEROLE_CONTROLLING);
  lport->SetIceTiebreaker(kTiebreaker1);
  rport->SetIceRole(cricket::ICEROLE_CONTROLLED);
  rport->SetIceTiebreaker(kTiebreaker2);

  lport->PrepareAddress();
  rport->PrepareAddress();
  ASSERT_FALSE(rport->Candidates().empty());

  // A client socket.
  FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket();
  socket->local_address_ = kLocalAddr1;
  socket->remote_address_ = kLocalAddr2;
  socket_factory.set_next_client_tcp_socket(socket);
  Connection* lconn =
      lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE);
  ASSERT_NE(lconn, nullptr);
  socket->SignalConnect(socket);
  lconn->Ping(0);

  // Now disconnect the client socket...
  socket->NotifyClosedForTest(1);

  // And prevent new sockets from being created.
  socket_factory.set_next_client_tcp_socket(nullptr);

  // Test that Ping() does not cause SEGV.
  lconn->Ping(0);
}

void PortTest::TestCrossFamilyPorts(int type) {
  FakePacketSocketFactory factory;
  std::unique_ptr<Port> ports[4];
  SocketAddress addresses[4] = {
      SocketAddress("192.168.1.3", 0), SocketAddress("192.168.1.4", 0),
      SocketAddress("2001:db8::1", 0), SocketAddress("2001:db8::2", 0)};
  for (int i = 0; i < 4; i++) {
    if (type == SOCK_DGRAM) {
      FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket();
      factory.set_next_udp_socket(socket);
      ports[i] = CreateUdpPort(addresses[i], &factory);
      socket->set_state(AsyncPacketSocket::STATE_BINDING);
      socket->SignalAddressReady(socket, addresses[i]);
    } else if (type == SOCK_STREAM) {
      FakeAsyncListenSocket* socket = new FakeAsyncListenSocket();
      factory.set_next_server_tcp_socket(socket);
      ports[i] = CreateTcpPort(addresses[i], &factory);
      socket->Bind(addresses[i]);
    }
    ports[i]->PrepareAddress();
  }

  // IPv4 Port, connects to IPv6 candidate and then to IPv4 candidate.
  if (type == SOCK_STREAM) {
    FakeAsyncPacketSocket* clientsocket = new FakeAsyncPacketSocket();
    factory.set_next_client_tcp_socket(clientsocket);
  }
  Connection* c = ports[0]->CreateConnection(GetCandidate(ports[2].get()),
                                             Port::ORIGIN_MESSAGE);
  EXPECT_TRUE(NULL == c);
  EXPECT_EQ(0U, ports[0]->connections().size());
  c = ports[0]->CreateConnection(GetCandidate(ports[1].get()),
                                 Port::ORIGIN_MESSAGE);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge