From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../transport/test/test_nr_socket_unittest.cpp | 800 +++++++++++++++++++++ 1 file changed, 800 insertions(+) create mode 100644 dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp (limited to 'dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp') diff --git a/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp new file mode 100644 index 0000000000..af2779accd --- /dev/null +++ b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp @@ -0,0 +1,800 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Original author: bcampen@mozilla.com + +#include + +extern "C" { +#include "r_errors.h" +#include "async_wait.h" +} + +#include "test_nr_socket.h" + +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "runnable_utils.h" + +#include + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +#define DATA_BUF_SIZE 1024 + +namespace mozilla { + +class TestNrSocketTest : public MtransportTest { + public: + TestNrSocketTest() + : MtransportTest(), + wait_done_for_main_(false), + sts_(), + public_addrs_(), + private_addrs_(), + nats_() {} + + void SetUp() override { + MtransportTest::SetUp(); + + // Get the transport service as a dispatch target + nsresult rv; + sts_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + EXPECT_TRUE(NS_SUCCEEDED(rv)) << "Failed to get STS: " << (int)rv; + } + + void TearDown() override { + SyncDispatchToSTS(WrapRunnable(this, &TestNrSocketTest::TearDown_s)); + + MtransportTest::TearDown(); + } + + void TearDown_s() { + public_addrs_.clear(); + private_addrs_.clear(); + nats_.clear(); + sts_ = nullptr; + } + + RefPtr CreateTestNrSocket_s(const char* ip_str, int proto, + TestNat* nat) { + // If no nat is supplied, we create a default NAT which is disabled. This + // is how we simulate a non-natted socket. + RefPtr sock(new TestNrSocket(nat ? nat : new TestNat)); + nr_transport_addr address; + nr_str_port_to_transport_addr(ip_str, 0, proto, &address); + int r = sock->create(&address); + if (r) { + return nullptr; + } + return sock; + } + + void CreatePublicAddrs(size_t count, const char* ip_str = "127.0.0.1", + int proto = IPPROTO_UDP) { + SyncDispatchToSTS(WrapRunnable(this, &TestNrSocketTest::CreatePublicAddrs_s, + count, ip_str, proto)); + } + + void CreatePublicAddrs_s(size_t count, const char* ip_str, int proto) { + while (count--) { + auto sock = CreateTestNrSocket_s(ip_str, proto, nullptr); + ASSERT_TRUE(sock) + << "Failed to create socket"; + public_addrs_.push_back(sock); + } + } + + RefPtr CreatePrivateAddrs(size_t size, + const char* ip_str = "127.0.0.1", + int proto = IPPROTO_UDP) { + RefPtr result; + SyncDispatchToSTS(WrapRunnableRet(&result, this, + &TestNrSocketTest::CreatePrivateAddrs_s, + size, ip_str, proto)); + return result; + } + + RefPtr CreatePrivateAddrs_s(size_t count, const char* ip_str, + int proto) { + RefPtr nat(new TestNat); + while (count--) { + auto sock = CreateTestNrSocket_s(ip_str, proto, nat); + if (!sock) { + EXPECT_TRUE(false) << "Failed to create socket"; + break; + } + private_addrs_.push_back(sock); + } + nat->enabled_ = true; + nats_.push_back(nat); + return nat; + } + + bool CheckConnectivityVia( + TestNrSocket* from, TestNrSocket* to, const nr_transport_addr& via, + nr_transport_addr* sender_external_address = nullptr) { + MOZ_ASSERT(from); + + if (!WaitForWriteable(from)) { + return false; + } + + int result = 0; + SyncDispatchToSTS(WrapRunnableRet( + &result, this, &TestNrSocketTest::SendData_s, from, via)); + if (result) { + return false; + } + + if (!WaitForReadable(to)) { + return false; + } + + nr_transport_addr dummy_outparam; + if (!sender_external_address) { + sender_external_address = &dummy_outparam; + } + + MOZ_ASSERT(to); + SyncDispatchToSTS(WrapRunnableRet(&result, this, + &TestNrSocketTest::RecvData_s, to, + sender_external_address)); + + return !result; + } + + bool CheckConnectivity(TestNrSocket* from, TestNrSocket* to, + nr_transport_addr* sender_external_address = nullptr) { + nr_transport_addr destination_address; + int r = GetAddress(to, &destination_address); + if (r) { + return false; + } + + return CheckConnectivityVia(from, to, destination_address, + sender_external_address); + } + + bool CheckTcpConnectivity(TestNrSocket* from, TestNrSocket* to) { + NrSocketBase* accepted_sock; + if (!Connect(from, to, &accepted_sock)) { + std::cerr << "Connect failed" << std::endl; + return false; + } + + // write on |from|, recv on |accepted_sock| + if (!WaitForWriteable(from)) { + std::cerr << __LINE__ << "WaitForWriteable (1) failed" << std::endl; + return false; + } + + int r; + SyncDispatchToSTS( + WrapRunnableRet(&r, this, &TestNrSocketTest::SendDataTcp_s, from)); + if (r) { + std::cerr << "SendDataTcp_s (1) failed" << std::endl; + return false; + } + + if (!WaitForReadable(accepted_sock)) { + std::cerr << __LINE__ << "WaitForReadable (1) failed" << std::endl; + return false; + } + + SyncDispatchToSTS(WrapRunnableRet( + &r, this, &TestNrSocketTest::RecvDataTcp_s, accepted_sock)); + if (r) { + std::cerr << "RecvDataTcp_s (1) failed" << std::endl; + return false; + } + + if (!WaitForWriteable(accepted_sock)) { + std::cerr << __LINE__ << "WaitForWriteable (2) failed" << std::endl; + return false; + } + + SyncDispatchToSTS(WrapRunnableRet( + &r, this, &TestNrSocketTest::SendDataTcp_s, accepted_sock)); + if (r) { + std::cerr << "SendDataTcp_s (2) failed" << std::endl; + return false; + } + + if (!WaitForReadable(from)) { + std::cerr << __LINE__ << "WaitForReadable (2) failed" << std::endl; + return false; + } + + SyncDispatchToSTS( + WrapRunnableRet(&r, this, &TestNrSocketTest::RecvDataTcp_s, from)); + if (r) { + std::cerr << "RecvDataTcp_s (2) failed" << std::endl; + return false; + } + + return true; + } + + int GetAddress(TestNrSocket* sock, nr_transport_addr_* address) { + MOZ_ASSERT(sock); + MOZ_ASSERT(address); + int r; + SyncDispatchToSTS(WrapRunnableRet(&r, this, &TestNrSocketTest::GetAddress_s, + sock, address)); + return r; + } + + int GetAddress_s(TestNrSocket* sock, nr_transport_addr* address) { + return sock->getaddr(address); + } + + int SendData_s(TestNrSocket* from, const nr_transport_addr& to) { + // It is up to caller to ensure that |from| is writeable. + const char buf[] = "foobajooba"; + return from->sendto(buf, sizeof(buf), 0, &to); + } + + int SendDataTcp_s(NrSocketBase* from) { + // It is up to caller to ensure that |from| is writeable. + const char buf[] = "foobajooba"; + size_t written; + return from->write(buf, sizeof(buf), &written); + } + + int RecvData_s(TestNrSocket* to, nr_transport_addr* from) { + // It is up to caller to ensure that |to| is readable + char buf[DATA_BUF_SIZE]; + size_t len; + // Maybe check that data matches? + int r = to->recvfrom(buf, sizeof(buf), &len, 0, from); + if (!r && (len == 0)) { + r = R_INTERNAL; + } + return r; + } + + int RecvDataTcp_s(NrSocketBase* to) { + // It is up to caller to ensure that |to| is readable + char buf[DATA_BUF_SIZE]; + size_t len; + // Maybe check that data matches? + int r = to->read(buf, sizeof(buf), &len); + if (!r && (len == 0)) { + r = R_INTERNAL; + } + return r; + } + + int Listen_s(TestNrSocket* to) { + // listen on |to| + int r = to->listen(1); + if (r) { + return r; + } + return 0; + } + + int Connect_s(TestNrSocket* from, TestNrSocket* to) { + // connect on |from| + nr_transport_addr destination_address; + int r = to->getaddr(&destination_address); + if (r) { + return r; + } + + r = from->connect(&destination_address); + if (r) { + return r; + } + + return 0; + } + + int Accept_s(TestNrSocket* to, NrSocketBase** accepted_sock) { + nr_socket* sock; + nr_transport_addr source_address; + int r = to->accept(&source_address, &sock); + if (r) { + return r; + } + + *accepted_sock = reinterpret_cast(sock->obj); + return 0; + } + + bool Connect(TestNrSocket* from, TestNrSocket* to, + NrSocketBase** accepted_sock) { + int r; + SyncDispatchToSTS( + WrapRunnableRet(&r, this, &TestNrSocketTest::Listen_s, to)); + if (r) { + std::cerr << "Listen_s failed: " << r << std::endl; + return false; + } + + SyncDispatchToSTS( + WrapRunnableRet(&r, this, &TestNrSocketTest::Connect_s, from, to)); + if (r && r != R_WOULDBLOCK) { + std::cerr << "Connect_s failed: " << r << std::endl; + return false; + } + + if (!WaitForReadable(to)) { + std::cerr << "WaitForReadable failed" << std::endl; + return false; + } + + SyncDispatchToSTS(WrapRunnableRet(&r, this, &TestNrSocketTest::Accept_s, to, + accepted_sock)); + + if (r) { + std::cerr << "Accept_s failed: " << r << std::endl; + return false; + } + return true; + } + + bool WaitForSocketState(NrSocketBase* sock, int state) { + MOZ_ASSERT(sock); + SyncDispatchToSTS(WrapRunnable( + this, &TestNrSocketTest::WaitForSocketState_s, sock, state)); + + bool res; + WAIT_(wait_done_for_main_, 500, res); + wait_done_for_main_ = false; + + if (!res) { + SyncDispatchToSTS( + WrapRunnable(this, &TestNrSocketTest::CancelWait_s, sock, state)); + } + + return res; + } + + void WaitForSocketState_s(NrSocketBase* sock, int state) { + NR_ASYNC_WAIT(sock, state, &WaitDone, this); + } + + void CancelWait_s(NrSocketBase* sock, int state) { sock->cancel(state); } + + bool WaitForReadable(NrSocketBase* sock) { + return WaitForSocketState(sock, NR_ASYNC_WAIT_READ); + } + + bool WaitForWriteable(NrSocketBase* sock) { + return WaitForSocketState(sock, NR_ASYNC_WAIT_WRITE); + } + + void SyncDispatchToSTS(nsIRunnable* runnable) { + NS_DispatchAndSpinEventLoopUntilComplete( + "TestNrSocketTest::SyncDispatchToSTS"_ns, sts_, do_AddRef(runnable)); + } + + static void WaitDone(void* sock, int how, void* test_fixture) { + TestNrSocketTest* test = static_cast(test_fixture); + test->wait_done_for_main_ = true; + } + + // Simple busywait boolean for the test cases to spin on. + Atomic wait_done_for_main_; + + nsCOMPtr sts_; + std::vector> public_addrs_; + std::vector> private_addrs_; + std::vector> nats_; +}; + +} // namespace mozilla + +using mozilla::NrSocketBase; +using mozilla::TestNat; +using mozilla::TestNrSocketTest; + +TEST_F(TestNrSocketTest, UnsafePortRejectedUDP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // ssh + 22, IPPROTO_UDP, &address)); + ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, UnsafePortRejectedTCP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // ssh + 22, IPPROTO_TCP, &address)); + ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, SafePortAcceptedUDP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // stuns + 5349, IPPROTO_UDP, &address)); + ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, SafePortAcceptedTCP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // turns + 5349, IPPROTO_TCP, &address)); + ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, PublicConnectivity) { + CreatePublicAddrs(2); + + ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateConnectivity) { + RefPtr nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, NoConnectivityWithoutPinhole) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1); + + ASSERT_FALSE(CheckConnectivity(public_addrs_[0], private_addrs_[0])); +} + +TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnets) { + RefPtr nat1(CreatePrivateAddrs(1)); + nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + RefPtr nat2(CreatePrivateAddrs(1)); + nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_FALSE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_FALSE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, FullConeAcceptIngress) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, FullConeOnePinhole) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; +} + +// OS 10.6 doesn't seem to allow us to open ports on 127.0.0.2, and while linux +// does allow this, it has other behavior (see below) that prevents this test +// from working. +TEST_F(TestNrSocketTest, DISABLED_AddressRestrictedCone) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ADDRESS_DEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2, "127.0.0.1"); + CreatePublicAddrs(1, "127.0.0.2"); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that another address on the same host can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Linux has a tendency to monkey around with source addresses, doing + // stuff like substituting 127.0.0.1 for packets sent by 127.0.0.2, and even + // going as far as substituting localhost for a packet sent from a real IP + // address when the destination is localhost. The only way to make this test + // work on linux is to have two real IP addresses. +#ifndef __linux__ + // Verify that an address on a different host can't use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[2], private_addrs_[0], + sender_external_address)); +#endif + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address3; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[2], + &sender_external_address3)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address3, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address3.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[2], private_addrs_[0], + sender_external_address3)); +} + +TEST_F(TestNrSocketTest, RestrictedCone) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::PORT_DEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP cannot use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); +} + +TEST_F(TestNrSocketTest, PortDependentMappingFullCone) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::PORT_DEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address0; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address0)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address0)); + + // Verify that other public IP can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address0)); + + // Send traffic to other public IP, verify that it uses a different pinhole + nr_transport_addr sender_external_address1; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address1)); + ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address0, + &sender_external_address1, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address0.as_string + << " addr2: " << sender_external_address1.as_string; + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address1)); + + // Verify that other public IP can use the original pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address1)); +} + +TEST_F(TestNrSocketTest, Symmetric) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::PORT_DEPENDENT; + nat->mapping_type_ = TestNat::PORT_DEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP cannot use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses a new pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)); + + // Verify that the other public IP can use the new pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); +} + +TEST_F(TestNrSocketTest, BlockUdp) { + RefPtr nat(CreatePrivateAddrs(2)); + nat->block_udp_ = true; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + ASSERT_FALSE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Make sure UDP behind the NAT still works + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); +} + +TEST_F(TestNrSocketTest, DenyHairpinning) { + RefPtr nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that hairpinning is disallowed + ASSERT_FALSE(CheckConnectivityVia(private_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, AllowHairpinning) { + RefPtr nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_timeout_ = 30000; + nat->allow_hairpinning_ = true; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0, obtain external address + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that hairpinning is allowed + ASSERT_TRUE(CheckConnectivityVia(private_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, FullConeTimeout) { + RefPtr nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_timeout_ = 200; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + PR_Sleep(201); + + // Verify that return traffic does not work + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, PublicConnectivityTcp) { + CreatePublicAddrs(2, "127.0.0.1", IPPROTO_TCP); + + ASSERT_TRUE(CheckTcpConnectivity(public_addrs_[0], public_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateConnectivityTcp) { + RefPtr nat(CreatePrivateAddrs(2, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateToPublicConnectivityTcp) { + RefPtr nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP); + + ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], public_addrs_[0])); +} + +TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnetsTcp) { + RefPtr nat1(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + RefPtr nat2(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_FALSE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, NoConnectivityPublicToPrivateTcp) { + RefPtr nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP); + + ASSERT_FALSE(CheckTcpConnectivity(public_addrs_[0], private_addrs_[0])); +} -- cgit v1.2.3