зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1035468: A NAT simulator based on NrSocket, and integrate into ice_unittest. r=ekr
--HG-- extra : rebase_source : 1ca1bb919526b3a4e091022e5aba04140efc9c90 extra : source : d354fa12177226f079090cc34277aaf1016e23fa
This commit is contained in:
Родитель
dc50e12fdd
Коммит
dc3971b1c9
|
@ -16,6 +16,7 @@ mtransport_lcppsrcs = [
|
|||
'rlogringbuffer.cpp',
|
||||
'simpletokenbucket.cpp',
|
||||
'stun_udp_socket_filter.cpp',
|
||||
'test_nr_socket.cpp',
|
||||
'transportflow.cpp',
|
||||
'transportlayer.cpp',
|
||||
'transportlayerdtls.cpp',
|
||||
|
|
|
@ -653,7 +653,7 @@ int NrSocket::sendto(const void *msg, size_t len,
|
|||
if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
|
||||
ABORT(R_WOULDBLOCK);
|
||||
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string);
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Error in sendto: %s", to->as_string);
|
||||
ABORT(R_IO_ERROR);
|
||||
}
|
||||
|
||||
|
@ -674,7 +674,7 @@ int NrSocket::recvfrom(void * buf, size_t maxlen,
|
|||
if (status <= 0) {
|
||||
if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
|
||||
ABORT(R_WOULDBLOCK);
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom");
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
|
||||
ABORT(R_IO_ERROR);
|
||||
}
|
||||
*len=status;
|
||||
|
@ -1309,7 +1309,7 @@ static nr_socket_vtbl nr_socket_local_vtbl={
|
|||
nr_socket_local_close
|
||||
};
|
||||
|
||||
int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
|
||||
int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) {
|
||||
RefPtr<NrSocketBase> sock;
|
||||
|
||||
// create IPC bridge for content process
|
||||
|
|
|
@ -116,6 +116,9 @@ public:
|
|||
|
||||
static TimeStamp short_term_violation_time();
|
||||
static TimeStamp long_term_violation_time();
|
||||
const nr_transport_addr& my_addr() const {
|
||||
return my_addr_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void fire_callback(int how);
|
||||
|
@ -163,7 +166,7 @@ public:
|
|||
virtual int write(const void *msg, size_t len, size_t *written) override;
|
||||
virtual int read(void* buf, size_t maxlen, size_t *len) override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
virtual ~NrSocket() {
|
||||
if (fd_)
|
||||
PR_Close(fd_);
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
#include "rlogringbuffer.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "stunserver.h"
|
||||
#include "nr_socket_prsock.h"
|
||||
#include "test_nr_socket.h"
|
||||
#include "ice_ctx.h"
|
||||
// TODO(bcampen@mozilla.com): Big fat hack since the build system doesn't give
|
||||
// us a clean way to add object files to a single executable.
|
||||
#include "stunserver.cpp"
|
||||
|
@ -250,13 +253,21 @@ class IceTestPeer : public sigslot::has_slots<> {
|
|||
expected_remote_type_(NrIceCandidate::ICE_HOST),
|
||||
trickle_mode_(TRICKLE_NONE),
|
||||
trickled_(0),
|
||||
simulate_ice_lite_(false) {
|
||||
simulate_ice_lite_(false),
|
||||
nat_(new TestNat) {
|
||||
ice_ctx_->SignalGatheringStateChange.connect(
|
||||
this,
|
||||
&IceTestPeer::GatheringStateChange);
|
||||
ice_ctx_->SignalConnectionStateChange.connect(
|
||||
this,
|
||||
&IceTestPeer::ConnectionStateChange);
|
||||
|
||||
nr_socket_factory *fac;
|
||||
int r = nat_->create_socket_factory(&fac);
|
||||
MOZ_ASSERT(!r);
|
||||
if (!r) {
|
||||
nr_ice_ctx_set_socket_factory(ice_ctx_->ctx(), fac);
|
||||
}
|
||||
}
|
||||
|
||||
~IceTestPeer() {
|
||||
|
@ -317,6 +328,11 @@ class IceTestPeer : public sigslot::has_slots<> {
|
|||
ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
|
||||
}
|
||||
|
||||
void UseTestStunServer() {
|
||||
SetStunServer(TestStunServer::GetInstance()->addr(),
|
||||
TestStunServer::GetInstance()->port());
|
||||
}
|
||||
|
||||
void SetTurnServer(const std::string addr, uint16_t port,
|
||||
const std::string username,
|
||||
const std::string password,
|
||||
|
@ -371,6 +387,25 @@ class IceTestPeer : public sigslot::has_slots<> {
|
|||
ASSERT_TRUE(NS_SUCCEEDED(res));
|
||||
}
|
||||
|
||||
void UseNat() {
|
||||
nat_->enabled_ = true;
|
||||
}
|
||||
|
||||
void SetFilteringType(TestNat::NatBehavior type) {
|
||||
MOZ_ASSERT(!nat_->has_port_mappings());
|
||||
nat_->filtering_type_ = type;
|
||||
}
|
||||
|
||||
void SetMappingType(TestNat::NatBehavior type) {
|
||||
MOZ_ASSERT(!nat_->has_port_mappings());
|
||||
nat_->mapping_type_ = type;
|
||||
}
|
||||
|
||||
void SetBlockUdp(bool block) {
|
||||
MOZ_ASSERT(!nat_->has_port_mappings());
|
||||
nat_->block_udp_ = block;
|
||||
}
|
||||
|
||||
// Get various pieces of state
|
||||
std::vector<std::string> GetGlobalAttributes() {
|
||||
std::vector<std::string> attrs(ice_ctx_->GetGlobalAttributes());
|
||||
|
@ -1008,6 +1043,7 @@ class IceTestPeer : public sigslot::has_slots<> {
|
|||
TrickleMode trickle_mode_;
|
||||
int trickled_;
|
||||
bool simulate_ice_lite_;
|
||||
nsRefPtr<mozilla::TestNat> nat_;
|
||||
};
|
||||
|
||||
void SchedulableTrickleCandidate::Trickle() {
|
||||
|
@ -1065,6 +1101,12 @@ class IceGatherTest : public ::testing::Test {
|
|||
TestStunServer::GetInstance()->port());
|
||||
}
|
||||
|
||||
void UseTestStunServer() {
|
||||
TestStunServer::GetInstance()->Reset();
|
||||
peer_->SetStunServer(TestStunServer::GetInstance()->addr(),
|
||||
TestStunServer::GetInstance()->port());
|
||||
}
|
||||
|
||||
// NB: Only does substring matching, watch out for stuff like "1.2.3.4"
|
||||
// matching "21.2.3.47". " 1.2.3.4 " should not have false positives.
|
||||
bool StreamHasMatchingCandidate(unsigned int stream,
|
||||
|
@ -1084,7 +1126,12 @@ class IceGatherTest : public ::testing::Test {
|
|||
|
||||
class IceConnectTest : public ::testing::Test {
|
||||
public:
|
||||
IceConnectTest() : initted_(false) {}
|
||||
IceConnectTest() :
|
||||
initted_(false),
|
||||
use_nat_(false),
|
||||
filtering_type_(TestNat::ENDPOINT_INDEPENDENT),
|
||||
mapping_type_(TestNat::ENDPOINT_INDEPENDENT),
|
||||
block_udp_(false) {}
|
||||
|
||||
void SetUp() {
|
||||
nsresult rv;
|
||||
|
@ -1126,8 +1173,25 @@ class IceConnectTest : public ::testing::Test {
|
|||
|
||||
bool Gather(unsigned int waitTime = kDefaultTimeout) {
|
||||
Init(false, false);
|
||||
p1_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
|
||||
p2_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
|
||||
if (use_nat_) {
|
||||
// If we enable nat simulation, but still use a real STUN server somewhere
|
||||
// on the internet, we will see failures if there is a real NAT in
|
||||
// addition to our simulated one, particularly if it disallows
|
||||
// hairpinning.
|
||||
UseTestStunServer();
|
||||
p1_->UseNat();
|
||||
p2_->UseNat();
|
||||
p1_->SetFilteringType(filtering_type_);
|
||||
p2_->SetFilteringType(filtering_type_);
|
||||
p1_->SetMappingType(mapping_type_);
|
||||
p2_->SetMappingType(mapping_type_);
|
||||
p1_->SetBlockUdp(block_udp_);
|
||||
p2_->SetBlockUdp(block_udp_);
|
||||
} else {
|
||||
p1_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
|
||||
p2_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
|
||||
}
|
||||
|
||||
p1_->Gather();
|
||||
p2_->Gather();
|
||||
|
||||
|
@ -1142,6 +1206,28 @@ class IceConnectTest : public ::testing::Test {
|
|||
return true;
|
||||
}
|
||||
|
||||
void UseNat() {
|
||||
use_nat_ = true;
|
||||
}
|
||||
|
||||
void SetFilteringType(TestNat::NatBehavior type) {
|
||||
filtering_type_ = type;
|
||||
}
|
||||
|
||||
void SetMappingType(TestNat::NatBehavior type) {
|
||||
mapping_type_ = type;
|
||||
}
|
||||
|
||||
void BlockUdp() {
|
||||
block_udp_ = true;
|
||||
}
|
||||
|
||||
void UseTestStunServer() {
|
||||
TestStunServer::GetInstance()->Reset();
|
||||
p1_->UseTestStunServer();
|
||||
p2_->UseTestStunServer();
|
||||
}
|
||||
|
||||
void SetTurnServer(const std::string addr, uint16_t port,
|
||||
const std::string username,
|
||||
const std::string password,
|
||||
|
@ -1277,6 +1363,10 @@ class IceConnectTest : public ::testing::Test {
|
|||
nsCOMPtr<nsIEventTarget> target_;
|
||||
mozilla::ScopedDeletePtr<IceTestPeer> p1_;
|
||||
mozilla::ScopedDeletePtr<IceTestPeer> p2_;
|
||||
bool use_nat_;
|
||||
TestNat::NatBehavior filtering_type_;
|
||||
TestNat::NatBehavior mapping_type_;
|
||||
bool block_udp_;
|
||||
};
|
||||
|
||||
class PrioritizerTest : public ::testing::Test {
|
||||
|
@ -1643,6 +1733,147 @@ TEST_F(IceConnectTest, TestTrickleIceLiteOfferer) {
|
|||
AssertCheckingReached();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherFullCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
ASSERT_TRUE(Gather());
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherFullConeAutoPrioritize) {
|
||||
Init(false, true);
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
ASSERT_TRUE(Gather());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectFullCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
|
||||
NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
|
||||
ASSERT_TRUE(Gather());
|
||||
Connect();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherAddressRestrictedCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::ADDRESS_DEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
ASSERT_TRUE(Gather());
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectAddressRestrictedCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::ADDRESS_DEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
|
||||
NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
|
||||
ASSERT_TRUE(Gather());
|
||||
Connect();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherPortRestrictedCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::PORT_DEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
ASSERT_TRUE(Gather());
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectPortRestrictedCone) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::PORT_DEPENDENT);
|
||||
SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
|
||||
SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
|
||||
NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
|
||||
ASSERT_TRUE(Gather());
|
||||
Connect();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherSymmetricNat) {
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::PORT_DEPENDENT);
|
||||
SetMappingType(TestNat::PORT_DEPENDENT);
|
||||
ASSERT_TRUE(Gather());
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectSymmetricNat) {
|
||||
if (g_turn_server.empty())
|
||||
return;
|
||||
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
SetFilteringType(TestNat::PORT_DEPENDENT);
|
||||
SetMappingType(TestNat::PORT_DEPENDENT);
|
||||
p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
|
||||
NrIceCandidate::Type::ICE_RELAYED);
|
||||
p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
|
||||
NrIceCandidate::Type::ICE_RELAYED);
|
||||
SetTurnServer(g_turn_server, kDefaultStunServerPort,
|
||||
g_turn_user, g_turn_password);
|
||||
ASSERT_TRUE(Gather());
|
||||
Connect();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestGatherNatBlocksUDP) {
|
||||
if (g_turn_server.empty())
|
||||
return;
|
||||
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
BlockUdp();
|
||||
std::vector<NrIceTurnServer> turn_servers;
|
||||
std::vector<unsigned char> password_vec(g_turn_password.begin(),
|
||||
g_turn_password.end());
|
||||
turn_servers.push_back(
|
||||
*NrIceTurnServer::Create(g_turn_server, kDefaultStunServerPort,
|
||||
g_turn_user, password_vec, kNrIceTransportTcp));
|
||||
turn_servers.push_back(
|
||||
*NrIceTurnServer::Create(g_turn_server, kDefaultStunServerPort,
|
||||
g_turn_user, password_vec, kNrIceTransportUdp));
|
||||
SetTurnServers(turn_servers);
|
||||
// We have to wait for the UDP-based stuff to time out.
|
||||
ASSERT_TRUE(Gather(kDefaultTimeout * 3));
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectNatBlocksUDP) {
|
||||
if (g_turn_server.empty())
|
||||
return;
|
||||
|
||||
AddStream("first", 1);
|
||||
UseNat();
|
||||
BlockUdp();
|
||||
std::vector<NrIceTurnServer> turn_servers;
|
||||
std::vector<unsigned char> password_vec(g_turn_password.begin(),
|
||||
g_turn_password.end());
|
||||
turn_servers.push_back(
|
||||
*NrIceTurnServer::Create(g_turn_server, kDefaultStunServerPort,
|
||||
g_turn_user, password_vec, kNrIceTransportTcp));
|
||||
turn_servers.push_back(
|
||||
*NrIceTurnServer::Create(g_turn_server, kDefaultStunServerPort,
|
||||
g_turn_user, password_vec, kNrIceTransportUdp));
|
||||
SetTurnServers(turn_servers);
|
||||
p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
|
||||
NrIceCandidate::Type::ICE_RELAYED,
|
||||
kNrIceTransportTcp);
|
||||
p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
|
||||
NrIceCandidate::Type::ICE_RELAYED,
|
||||
kNrIceTransportTcp);
|
||||
ASSERT_TRUE(Gather(kDefaultTimeout * 3));
|
||||
Connect();
|
||||
}
|
||||
|
||||
TEST_F(IceConnectTest, TestConnectTwoComponents) {
|
||||
AddStream("first", 2);
|
||||
ASSERT_TRUE(Gather());
|
||||
|
|
|
@ -14,6 +14,7 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
|
|||
'runnable_utils_unittest',
|
||||
'simpletokenbucket_unittest',
|
||||
'sockettransportservice_unittest',
|
||||
'test_nr_socket_unittest',
|
||||
'TestSyncRunnable',
|
||||
'transport_unittests',
|
||||
'turn_unittest',
|
||||
|
@ -30,6 +31,8 @@ for var in ('MOZILLA_INTERNAL_API', 'MOZILLA_XPCOMRT_API', 'MOZILLA_EXTERNAL_LIN
|
|||
DEFINES[var] = True
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
DEFINES['LINUX'] = True
|
||||
DEFINES['ANDROID'] = True
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/mtransport/third_party/nrappkit/src/port/android/include',
|
||||
]
|
||||
|
@ -37,6 +40,8 @@ else:
|
|||
DEFINES['INET6'] = True
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Linux':
|
||||
DEFINES['LINUX'] = True
|
||||
DEFINES['USE_INTERFACE_PRIORITIZER'] = True
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/mtransport/third_party/nrappkit/src/port/linux/include',
|
||||
]
|
||||
|
@ -53,12 +58,20 @@ if CONFIG['OS_TARGET'] == 'Darwin':
|
|||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD'):
|
||||
if CONFIG['OS_TARGET'] == 'Darwin':
|
||||
DEFINES['DARWIN'] = True
|
||||
else:
|
||||
DEFINES['BSD'] = True
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/mtransport/third_party/nrappkit/src/port/darwin/include',
|
||||
]
|
||||
|
||||
# SCTP DEFINES
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
DEFINES['WIN'] = True
|
||||
# for stun.h
|
||||
DEFINES['WIN32'] = True
|
||||
DEFINES['NOMINMAX'] = True
|
||||
DEFINES['__Userspace_os_Windows'] = 1
|
||||
else:
|
||||
# Works for Darwin, Linux, Android. Probably doesn't work for others.
|
||||
|
|
|
@ -221,7 +221,7 @@ int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) {
|
|||
return R_INTERNAL;
|
||||
}
|
||||
|
||||
if (nr_socket_local_create(&addr->addr, &listen_sock_)) {
|
||||
if (nr_socket_local_create(nullptr, &addr->addr, &listen_sock_)) {
|
||||
MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket");
|
||||
return R_ALREADY;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,660 @@
|
|||
/* -*- 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
|
||||
|
||||
extern "C" {
|
||||
#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
|
||||
#include "stun_util.h"
|
||||
#include "nr_api.h"
|
||||
#include "async_wait.h"
|
||||
#include "nr_socket.h"
|
||||
#include "nr_socket_local.h"
|
||||
#include "stun_hint.h"
|
||||
#include "local_addr.h"
|
||||
#include "registry.h"
|
||||
}
|
||||
|
||||
#include "test_nr_socket.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "mtransport_test_utils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define GTEST_HAS_RTTI 0
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TestNrSocketTest : public ::testing::Test {
|
||||
public:
|
||||
TestNrSocketTest() :
|
||||
wait_done_for_main_(false),
|
||||
sts_(),
|
||||
public_addrs_(),
|
||||
private_addrs_(),
|
||||
nats_() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
~TestNrSocketTest() {
|
||||
sts_->Dispatch(WrapRunnable(this, &TestNrSocketTest::TearDown_s),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
void TearDown_s() {
|
||||
public_addrs_.clear();
|
||||
private_addrs_.clear();
|
||||
nats_.clear();
|
||||
sts_ = nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestNrSocket> CreateTestNrSocket_s(const char *ip_str,
|
||||
TestNat *nat) {
|
||||
// If no nat is supplied, we create a default NAT which is disabled. This
|
||||
// is how we simulate a non-natted socket.
|
||||
nsRefPtr<TestNrSocket> sock(new TestNrSocket(nat ? nat : new TestNat));
|
||||
nr_transport_addr address;
|
||||
nr_ip4_str_port_to_transport_addr(ip_str, 0, IPPROTO_UDP, &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") {
|
||||
sts_->Dispatch(
|
||||
WrapRunnable(this,
|
||||
&TestNrSocketTest::CreatePublicAddrs_s,
|
||||
count,
|
||||
ip_str),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
void CreatePublicAddrs_s(size_t count, const char* ip_str) {
|
||||
while (count--) {
|
||||
auto sock = CreateTestNrSocket_s(ip_str, nullptr);
|
||||
ASSERT_TRUE(sock) << "Failed to create socket";
|
||||
public_addrs_.push_back(sock);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<TestNat> CreatePrivateAddrs(size_t size,
|
||||
const char* ip_str = "127.0.0.1") {
|
||||
nsRefPtr<TestNat> result;
|
||||
sts_->Dispatch(
|
||||
WrapRunnableRet(this,
|
||||
&TestNrSocketTest::CreatePrivateAddrs_s,
|
||||
size,
|
||||
ip_str,
|
||||
&result),
|
||||
NS_DISPATCH_SYNC);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRefPtr<TestNat> CreatePrivateAddrs_s(size_t count, const char* ip_str) {
|
||||
nsRefPtr<TestNat> nat(new TestNat);
|
||||
while (count--) {
|
||||
auto sock = CreateTestNrSocket_s(ip_str, 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;
|
||||
sts_->Dispatch(WrapRunnableRet(this,
|
||||
&TestNrSocketTest::SendData_s,
|
||||
from,
|
||||
via,
|
||||
&result),
|
||||
NS_DISPATCH_SYNC);
|
||||
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);
|
||||
sts_->Dispatch(WrapRunnableRet(this,
|
||||
&TestNrSocketTest::RecvData_s,
|
||||
to,
|
||||
sender_external_address,
|
||||
&result),
|
||||
NS_DISPATCH_SYNC);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int GetAddress(TestNrSocket *sock, nr_transport_addr_ *address) {
|
||||
MOZ_ASSERT(sock);
|
||||
MOZ_ASSERT(address);
|
||||
int r;
|
||||
sts_->Dispatch(WrapRunnableRet(this,
|
||||
&TestNrSocketTest::GetAddress_s,
|
||||
sock,
|
||||
address,
|
||||
&r),
|
||||
NS_DISPATCH_SYNC);
|
||||
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,
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
const_cast<nr_transport_addr*>(&to));
|
||||
}
|
||||
|
||||
int RecvData_s(TestNrSocket *to, nr_transport_addr *from) {
|
||||
// It is up to caller to ensure that |to| is readable
|
||||
const size_t bufSize = 1024;
|
||||
char buf[bufSize];
|
||||
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;
|
||||
}
|
||||
|
||||
bool WaitForSocketState(TestNrSocket *sock, int state) {
|
||||
MOZ_ASSERT(sock);
|
||||
sts_->Dispatch(WrapRunnable(this,
|
||||
&TestNrSocketTest::WaitForSocketState_s,
|
||||
sock,
|
||||
state),
|
||||
NS_DISPATCH_SYNC);
|
||||
|
||||
bool res;
|
||||
WAIT_(wait_done_for_main_, 100, res);
|
||||
wait_done_for_main_ = false;
|
||||
|
||||
if (!res) {
|
||||
sts_->Dispatch(WrapRunnable(this,
|
||||
&TestNrSocketTest::CancelWait_s,
|
||||
sock,
|
||||
state),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void WaitForSocketState_s(TestNrSocket *sock, int state) {
|
||||
NR_ASYNC_WAIT(sock, state, &WaitDone, this);
|
||||
}
|
||||
|
||||
void CancelWait_s(TestNrSocket *sock, int state) {
|
||||
sock->cancel(state);
|
||||
}
|
||||
|
||||
bool WaitForReadable(TestNrSocket *sock) {
|
||||
return WaitForSocketState(sock, NR_ASYNC_WAIT_READ);
|
||||
}
|
||||
|
||||
bool WaitForWriteable(TestNrSocket *sock) {
|
||||
return WaitForSocketState(sock, NR_ASYNC_WAIT_WRITE);
|
||||
}
|
||||
|
||||
static void WaitDone(void *sock, int how, void *test_fixture) {
|
||||
TestNrSocketTest *test = static_cast<TestNrSocketTest*>(test_fixture);
|
||||
test->wait_done_for_main_ = true;
|
||||
}
|
||||
|
||||
// Simple busywait boolean for the test cases to spin on.
|
||||
Atomic<bool> wait_done_for_main_;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> sts_;
|
||||
std::vector<nsRefPtr<TestNrSocket>> public_addrs_;
|
||||
std::vector<nsRefPtr<TestNrSocket>> private_addrs_;
|
||||
std::vector<nsRefPtr<TestNat>> nats_;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
using mozilla::TestNrSocketTest;
|
||||
using mozilla::TestNat;
|
||||
|
||||
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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> nat1(CreatePrivateAddrs(1));
|
||||
nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
|
||||
nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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) {
|
||||
nsRefPtr<TestNat> 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));
|
||||
}
|
||||
|
||||
// TODO(): We need TCP tests, but first we will need ICE TCP to land (this
|
||||
// adds listen/accept support to NrSocket)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Inits STS and some other stuff.
|
||||
MtransportTestUtils test_utils;
|
||||
|
||||
NR_reg_init(NR_REG_MODE_LOCAL);
|
||||
|
||||
// Start the tests
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
int rv = RUN_ALL_TESTS();
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -119,7 +119,7 @@ class TurnClient : public ::testing::Test {
|
|||
r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
|
||||
ASSERT_EQ(0, r);
|
||||
|
||||
r = nr_socket_local_create(&addr, &real_socket_);
|
||||
r = nr_socket_local_create(nullptr, &addr, &real_socket_);
|
||||
ASSERT_EQ(0, r);
|
||||
|
||||
if (protocol_ == IPPROTO_TCP) {
|
||||
|
|
|
@ -0,0 +1,763 @@
|
|||
/* -*- 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/. */
|
||||
/*
|
||||
*/
|
||||
|
||||
/*
|
||||
Based partially on original code from nICEr and nrappkit.
|
||||
|
||||
nICEr copyright:
|
||||
|
||||
Copyright (c) 2007, Adobe Systems, Incorporated
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Adobe Systems, Network Resonance nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
nrappkit copyright:
|
||||
|
||||
Copyright (C) 2001-2003, Network Resonance, Inc.
|
||||
Copyright (C) 2006, Network Resonance, Inc.
|
||||
All Rights Reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of Network Resonance, Inc. nor the name of any
|
||||
contributors to this software may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
ekr@rtfm.com Thu Dec 20 20:14:49 2001
|
||||
*/
|
||||
|
||||
// Original author: bcampen@mozilla.com [:bwc]
|
||||
|
||||
extern "C" {
|
||||
#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
|
||||
#include "nr_api.h"
|
||||
#include "async_wait.h"
|
||||
#include "nr_socket.h"
|
||||
#include "nr_socket_local.h"
|
||||
#include "stun_hint.h"
|
||||
#include "transport_addr.h"
|
||||
}
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "test_nr_socket.h"
|
||||
#include "runnable_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static int test_nat_socket_create(void *obj,
|
||||
nr_transport_addr *addr,
|
||||
nr_socket **sockp) {
|
||||
RefPtr<NrSocketBase> sock = new TestNrSocket(static_cast<TestNat*>(obj));
|
||||
|
||||
int r, _status;
|
||||
|
||||
r = sock->create(addr);
|
||||
if (r)
|
||||
ABORT(r);
|
||||
|
||||
r = nr_socket_create_int(static_cast<void *>(sock),
|
||||
sock->vtbl(), sockp);
|
||||
if (r)
|
||||
ABORT(r);
|
||||
|
||||
_status = 0;
|
||||
|
||||
{
|
||||
// We will release this reference in destroy(), not exactly the normal
|
||||
// ownership model, but it is what it is.
|
||||
NrSocketBase *dummy = sock.forget().take();
|
||||
(void)dummy;
|
||||
}
|
||||
|
||||
abort:
|
||||
return _status;
|
||||
}
|
||||
|
||||
static int test_nat_socket_factory_destroy(void **obj) {
|
||||
TestNat *nat = static_cast<TestNat*>(*obj);
|
||||
*obj = nullptr;
|
||||
nat->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = {
|
||||
test_nat_socket_create,
|
||||
test_nat_socket_factory_destroy
|
||||
};
|
||||
|
||||
bool TestNat::has_port_mappings() const {
|
||||
for (TestNrSocket *sock : sockets_) {
|
||||
if (sock->has_port_mappings()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestNat::is_my_external_tuple(const nr_transport_addr &addr) const {
|
||||
for (TestNrSocket *sock : sockets_) {
|
||||
if (sock->is_my_external_tuple(addr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestNat::is_an_internal_tuple(const nr_transport_addr &addr) const {
|
||||
for (TestNrSocket *sock : sockets_) {
|
||||
nr_transport_addr addr_behind_nat;
|
||||
if (sock->getaddr(&addr_behind_nat)) {
|
||||
MOZ_CRASH("TestNrSocket::getaddr failed!");
|
||||
}
|
||||
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
|
||||
&addr_behind_nat,
|
||||
NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TestNat::create_socket_factory(nr_socket_factory **factorypp) {
|
||||
int r = nr_socket_factory_create_int(this,
|
||||
&test_nat_socket_factory_vtbl,
|
||||
factorypp);
|
||||
if (!r) {
|
||||
AddRef();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
TestNrSocket::TestNrSocket(TestNat *nat)
|
||||
: nat_(nat) {
|
||||
nat_->insert_socket(this);
|
||||
}
|
||||
|
||||
TestNrSocket::~TestNrSocket() {
|
||||
nat_->erase_socket(this);
|
||||
}
|
||||
|
||||
nsRefPtr<NrSocket> TestNrSocket::create_external_socket(
|
||||
const nr_transport_addr &dest_addr) const {
|
||||
MOZ_ASSERT(nat_->enabled_);
|
||||
MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
|
||||
|
||||
int r;
|
||||
nr_transport_addr nat_external_addr;
|
||||
|
||||
// Open the socket on an arbitrary port, on the same address.
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
if ((r = nr_transport_addr_copy(&nat_external_addr,
|
||||
const_cast<nr_transport_addr*>(&my_addr_)))) {
|
||||
r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
|
||||
__FUNCTION__, r);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) {
|
||||
r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_set_port: %d",
|
||||
__FUNCTION__, r);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<NrSocket> external_socket = new NrSocket;
|
||||
|
||||
if ((r = external_socket->create(&nat_external_addr))) {
|
||||
r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d",
|
||||
__FUNCTION__, r);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return external_socket;
|
||||
}
|
||||
|
||||
int TestNrSocket::sendto(const void *msg, size_t len,
|
||||
int flags, nr_transport_addr *to) {
|
||||
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP);
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
|
||||
return NrSocket::sendto(msg, len, flags, to);
|
||||
}
|
||||
|
||||
destroy_stale_port_mappings();
|
||||
|
||||
if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
|
||||
// Silently eat the packet
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Choose our port mapping based on our most selective criteria
|
||||
PortMapping *port_mapping = get_port_mapping(*to,
|
||||
std::max(nat_->filtering_type_,
|
||||
nat_->mapping_type_));
|
||||
|
||||
if (!port_mapping) {
|
||||
// See if we have already made the external socket we need to use.
|
||||
PortMapping *similar_port_mapping =
|
||||
get_port_mapping(*to, nat_->mapping_type_);
|
||||
nsRefPtr<NrSocket> external_socket;
|
||||
|
||||
if (similar_port_mapping) {
|
||||
external_socket = similar_port_mapping->external_socket_;
|
||||
} else {
|
||||
external_socket = create_external_socket(*to);
|
||||
if (!external_socket) {
|
||||
MOZ_ASSERT(false);
|
||||
return R_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
port_mapping = create_port_mapping(*to, external_socket);
|
||||
port_mappings_.push_back(port_mapping);
|
||||
|
||||
if (poll_flags() & PR_POLL_READ) {
|
||||
// Make sure the new port mapping is ready to receive traffic if the
|
||||
// TestNrSocket is already waiting.
|
||||
port_mapping->async_wait(NR_ASYNC_WAIT_READ,
|
||||
port_mapping_readable_callback,
|
||||
this,
|
||||
(char*)__FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
// We probably don't want to propagate the flags, since this is a simulated
|
||||
// external IP address.
|
||||
return port_mapping->sendto(msg, len, *to);
|
||||
}
|
||||
|
||||
int TestNrSocket::recvfrom(void *buf, size_t maxlen,
|
||||
size_t *len, int flags,
|
||||
nr_transport_addr *from) {
|
||||
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP);
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
int r;
|
||||
bool ingress_allowed = false;
|
||||
|
||||
if (readable_socket_) {
|
||||
// If any of the external sockets got data, see if it will be passed through
|
||||
r = readable_socket_->recvfrom(buf, maxlen, len, 0, from);
|
||||
readable_socket_ = nullptr;
|
||||
if (!r) {
|
||||
PortMapping *port_mapping_used;
|
||||
ingress_allowed = allow_ingress(*from, &port_mapping_used);
|
||||
if (ingress_allowed && nat_->refresh_on_ingress_ && port_mapping_used) {
|
||||
port_mapping_used->last_used_ = PR_IntervalNow();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no external socket has data, see if there's any data that was sent
|
||||
// directly to the TestNrSocket, and eat it if it isn't supposed to get
|
||||
// through.
|
||||
r = NrSocket::recvfrom(buf, maxlen, len, flags, from);
|
||||
if (!r) {
|
||||
// We do not use allow_ingress() here because that only handles traffic
|
||||
// landing on an external port.
|
||||
ingress_allowed = (!nat_->enabled_ ||
|
||||
nat_->is_an_internal_tuple(*from));
|
||||
if (!ingress_allowed) {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
|
||||
"Not behind the same NAT",
|
||||
my_addr_.as_string,
|
||||
from->as_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kinda lame that we are forced to give the app a readable callback and then
|
||||
// say "Oh, never mind...", but the alternative is to totally decouple the
|
||||
// callbacks from STS and the callbacks the app sets. On the bright side, this
|
||||
// speeds up unit tests where we are verifying that ingress is forbidden,
|
||||
// since they'll get a readable callback and then an error, instead of having
|
||||
// to wait for a timeout.
|
||||
if (!ingress_allowed) {
|
||||
*len = 0;
|
||||
r = R_WOULDBLOCK;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
|
||||
PortMapping **port_mapping_used) const {
|
||||
*port_mapping_used = nullptr;
|
||||
if (!nat_->enabled_)
|
||||
return true;
|
||||
|
||||
if (nat_->is_an_internal_tuple(from))
|
||||
return true;
|
||||
|
||||
*port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
|
||||
if (!(*port_mapping_used)) {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
|
||||
"Filtered",
|
||||
my_addr_.as_string,
|
||||
from.as_string);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_port_mapping_stale(**port_mapping_used)) {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
|
||||
"Stale port mapping",
|
||||
my_addr_.as_string,
|
||||
from.as_string);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
|
||||
"Hairpinning disallowed",
|
||||
my_addr_.as_string,
|
||||
from.as_string);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int TestNrSocket::connect(nr_transport_addr *addr) {
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
if (connect_invoked_ || !port_mappings_.empty()) {
|
||||
MOZ_CRASH("TestNrSocket::connect() called more than once!");
|
||||
return R_INTERNAL;
|
||||
}
|
||||
|
||||
if (!nat_->enabled_ || nat_->is_an_internal_tuple(*addr)) {
|
||||
// This will set connect_invoked_
|
||||
return NrSocket::connect(addr);
|
||||
}
|
||||
|
||||
nsRefPtr<NrSocket> external_socket(create_external_socket(*addr));
|
||||
if (!external_socket) {
|
||||
return R_INTERNAL;
|
||||
}
|
||||
|
||||
PortMapping *port_mapping = create_port_mapping(*addr, external_socket);
|
||||
port_mappings_.push_back(port_mapping);
|
||||
port_mapping->external_socket_->connect(addr);
|
||||
port_mapping->last_used_ = PR_IntervalNow();
|
||||
|
||||
if (poll_flags() & PR_POLL_READ) {
|
||||
port_mapping->async_wait(NR_ASYNC_WAIT_READ,
|
||||
port_mapping_tcp_passthrough_callback,
|
||||
this,
|
||||
(char*)__FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
if (port_mappings_.empty()) {
|
||||
// The no-nat case, just pass call through.
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s writing",
|
||||
my_addr().as_string);
|
||||
|
||||
return NrSocket::write(msg, len, written);
|
||||
} else {
|
||||
// This is TCP only
|
||||
MOZ_ASSERT(port_mappings_.size() == 1);
|
||||
r_log(LOG_GENERIC, LOG_INFO,
|
||||
"PortMapping %s -> %s writing",
|
||||
port_mappings_.front()->external_socket_->my_addr().as_string,
|
||||
port_mappings_.front()->remote_address_.as_string);
|
||||
|
||||
return port_mappings_.front()->external_socket_->write(msg, len, written);
|
||||
}
|
||||
}
|
||||
|
||||
int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
if (port_mappings_.empty()) {
|
||||
return NrSocket::read(buf, maxlen, len);
|
||||
} else {
|
||||
MOZ_ASSERT(port_mappings_.size() == 1);
|
||||
return port_mappings_.front()->external_socket_->read(buf, maxlen, len);
|
||||
}
|
||||
}
|
||||
|
||||
int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
|
||||
char *function, int line) {
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
// Make sure we're waiting on the socket for the internal address
|
||||
int r = NrSocket::async_wait(how, cb, cb_arg, function, line);
|
||||
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
|
||||
my_addr_.as_string,
|
||||
how == NR_ASYNC_WAIT_READ ? "read" : "write");
|
||||
|
||||
if (is_tcp_connection_behind_nat()) {
|
||||
// Bypass all port-mapping related logic
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (my_addr_.protocol == IPPROTO_TCP) {
|
||||
// For a TCP connection through a simulated NAT, these signals are
|
||||
// just passed through.
|
||||
MOZ_ASSERT(port_mappings_.size() == 1);
|
||||
|
||||
return port_mappings_.front()->async_wait(
|
||||
how,
|
||||
port_mapping_tcp_passthrough_callback,
|
||||
this,
|
||||
function,
|
||||
line);
|
||||
} else if (how == NR_ASYNC_WAIT_READ) {
|
||||
// For UDP port mappings, we decouple the writeable callbacks
|
||||
for (PortMapping *port_mapping : port_mappings_) {
|
||||
// Be ready to receive traffic on our port mappings
|
||||
r = port_mapping->async_wait(how,
|
||||
port_mapping_readable_callback,
|
||||
this,
|
||||
function,
|
||||
line);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TestNrSocket::cancel_port_mapping_async_wait(int how) {
|
||||
for (PortMapping *port_mapping : port_mappings_) {
|
||||
port_mapping->cancel(how);
|
||||
}
|
||||
}
|
||||
|
||||
int TestNrSocket::cancel(int how) {
|
||||
ASSERT_ON_THREAD(ststhread_);
|
||||
|
||||
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
|
||||
my_addr_.as_string,
|
||||
how == NR_ASYNC_WAIT_READ ? "read" : "write");
|
||||
|
||||
// Writable callbacks are decoupled except for the TCP case
|
||||
if (how == NR_ASYNC_WAIT_READ || my_addr_.protocol == IPPROTO_TCP) {
|
||||
cancel_port_mapping_async_wait(how);
|
||||
}
|
||||
|
||||
return NrSocket::cancel(how);
|
||||
}
|
||||
|
||||
bool TestNrSocket::has_port_mappings() const {
|
||||
return !port_mappings_.empty();
|
||||
}
|
||||
|
||||
bool TestNrSocket::is_my_external_tuple(const nr_transport_addr &addr) const {
|
||||
for (PortMapping *port_mapping : port_mappings_) {
|
||||
nr_transport_addr port_mapping_addr;
|
||||
if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) {
|
||||
MOZ_CRASH("NrSocket::getaddr failed!");
|
||||
}
|
||||
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
|
||||
&port_mapping_addr,
|
||||
NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestNrSocket::is_port_mapping_stale(
|
||||
const PortMapping &port_mapping) const {
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
PRIntervalTime elapsed_ticks = now - port_mapping.last_used_;
|
||||
uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks);
|
||||
return idle_duration > nat_->mapping_timeout_;
|
||||
}
|
||||
|
||||
void TestNrSocket::destroy_stale_port_mappings() {
|
||||
for (auto i = port_mappings_.begin(); i != port_mappings_.end();) {
|
||||
auto temp = i;
|
||||
++i;
|
||||
if (is_port_mapping_stale(**temp)) {
|
||||
r_log(LOG_GENERIC, LOG_INFO,
|
||||
"TestNrSocket %s destroying port mapping %s -> %s",
|
||||
my_addr_.as_string,
|
||||
(*temp)->external_socket_->my_addr().as_string,
|
||||
(*temp)->remote_address_.as_string);
|
||||
|
||||
port_mappings_.erase(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestNrSocket::port_mapping_readable_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v) {
|
||||
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
|
||||
NrSocket *external_socket = static_cast<NrSocket*>(ext_sock_v);
|
||||
|
||||
test_socket->on_port_mapping_readable(external_socket);
|
||||
}
|
||||
|
||||
void TestNrSocket::on_port_mapping_readable(NrSocket *external_socket) {
|
||||
if (!readable_socket_) {
|
||||
readable_socket_ = external_socket;
|
||||
}
|
||||
|
||||
// None of our port mappings should be waiting for readable callbacks
|
||||
// if nobody is waiting for readable callbacks from us.
|
||||
MOZ_ASSERT(poll_flags() & PR_POLL_READ);
|
||||
|
||||
fire_readable_callback();
|
||||
}
|
||||
|
||||
void TestNrSocket::fire_readable_callback() {
|
||||
MOZ_ASSERT(poll_flags() & PR_POLL_READ);
|
||||
// Stop listening on all mapped sockets; we will start listening again
|
||||
// if the app starts listening to us again.
|
||||
cancel_port_mapping_async_wait(NR_ASYNC_WAIT_READ);
|
||||
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
|
||||
my_addr_.as_string);
|
||||
fire_callback(NR_ASYNC_WAIT_READ);
|
||||
}
|
||||
|
||||
void TestNrSocket::port_mapping_writeable_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v) {
|
||||
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
|
||||
NrSocket *external_socket = static_cast<NrSocket*>(ext_sock_v);
|
||||
|
||||
test_socket->write_to_port_mapping(external_socket);
|
||||
}
|
||||
|
||||
void TestNrSocket::write_to_port_mapping(NrSocket *external_socket) {
|
||||
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP);
|
||||
|
||||
int r = 0;
|
||||
for (PortMapping *port_mapping : port_mappings_) {
|
||||
if (port_mapping->external_socket_ == external_socket) {
|
||||
// If the send succeeds, or if there was nothing to send, we keep going
|
||||
r = port_mapping->send_from_queue();
|
||||
if (r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r == R_WOULDBLOCK) {
|
||||
// Re-register for writeable callbacks, since we still have stuff to send
|
||||
NR_ASYNC_WAIT(external_socket,
|
||||
NR_ASYNC_WAIT_WRITE,
|
||||
&TestNrSocket::port_mapping_writeable_callback,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
void TestNrSocket::port_mapping_tcp_passthrough_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v) {
|
||||
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
|
||||
r_log(LOG_GENERIC, LOG_INFO,
|
||||
"TestNrSocket %s firing %s callback",
|
||||
test_socket->my_addr().as_string,
|
||||
how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
|
||||
|
||||
|
||||
test_socket->fire_callback(how);
|
||||
}
|
||||
|
||||
bool TestNrSocket::is_tcp_connection_behind_nat() const {
|
||||
return my_addr_.protocol == IPPROTO_TCP && port_mappings_.empty();
|
||||
}
|
||||
|
||||
TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
|
||||
const nr_transport_addr &remote_address,
|
||||
TestNat::NatBehavior filter) const {
|
||||
int compare_flags;
|
||||
switch (filter) {
|
||||
case TestNat::ENDPOINT_INDEPENDENT:
|
||||
compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL;
|
||||
break;
|
||||
case TestNat::ADDRESS_DEPENDENT:
|
||||
compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR;
|
||||
break;
|
||||
case TestNat::PORT_DEPENDENT:
|
||||
compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (PortMapping *port_mapping : port_mappings_) {
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&remote_address),
|
||||
&port_mapping->remote_address_,
|
||||
compare_flags))
|
||||
return port_mapping;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
|
||||
const nr_transport_addr &remote_address,
|
||||
const nsRefPtr<NrSocket> &external_socket) const {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
|
||||
my_addr_.as_string,
|
||||
external_socket->my_addr().as_string,
|
||||
remote_address.as_string);
|
||||
|
||||
return new PortMapping(remote_address, external_socket);
|
||||
}
|
||||
|
||||
TestNrSocket::PortMapping::PortMapping(
|
||||
const nr_transport_addr &remote_address,
|
||||
const nsRefPtr<NrSocket> &external_socket) :
|
||||
external_socket_(external_socket) {
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
nr_transport_addr_copy(&remote_address_,
|
||||
const_cast<nr_transport_addr*>(&remote_address));
|
||||
}
|
||||
|
||||
int TestNrSocket::PortMapping::send_from_queue() {
|
||||
MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
|
||||
int r = 0;
|
||||
|
||||
while (!send_queue_.empty()) {
|
||||
UdpPacket &packet = *send_queue_.front();
|
||||
r_log(LOG_GENERIC, LOG_INFO,
|
||||
"PortMapping %s -> %s sending from queue to %s",
|
||||
external_socket_->my_addr().as_string,
|
||||
remote_address_.as_string,
|
||||
packet.remote_address_.as_string);
|
||||
|
||||
r = external_socket_->sendto(packet.buffer_->data(),
|
||||
packet.buffer_->len(),
|
||||
0,
|
||||
&packet.remote_address_);
|
||||
|
||||
if (r) {
|
||||
if (r != R_WOULDBLOCK) {
|
||||
r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying",
|
||||
__FUNCTION__, r);
|
||||
send_queue_.clear();
|
||||
} else {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Would block, will retry later");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
send_queue_.pop_front();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int TestNrSocket::PortMapping::sendto(const void *msg,
|
||||
size_t len,
|
||||
const nr_transport_addr &to) {
|
||||
MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
|
||||
r_log(LOG_GENERIC, LOG_INFO,
|
||||
"PortMapping %s -> %s sending to %s",
|
||||
external_socket_->my_addr().as_string,
|
||||
remote_address_.as_string,
|
||||
to.as_string);
|
||||
|
||||
last_used_ = PR_IntervalNow();
|
||||
int r = external_socket_->sendto(msg, len, 0,
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
const_cast<nr_transport_addr*>(&to));
|
||||
|
||||
if (r == R_WOULDBLOCK) {
|
||||
r_log(LOG_GENERIC, LOG_INFO, "Enqueueing UDP packet to %s", to.as_string);
|
||||
send_queue_.push_back(nsRefPtr<UdpPacket>(new UdpPacket(msg, len, to)));
|
||||
return 0;
|
||||
} else if (r) {
|
||||
r_log(LOG_GENERIC,LOG_ERR, "Error: %d", r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void *cb_arg,
|
||||
char *function, int line) {
|
||||
r_log(LOG_GENERIC, LOG_DEBUG,
|
||||
"PortMapping %s -> %s waiting for %s",
|
||||
external_socket_->my_addr().as_string,
|
||||
remote_address_.as_string,
|
||||
how == NR_ASYNC_WAIT_READ ? "read" : "write");
|
||||
|
||||
return external_socket_->async_wait(how, cb, cb_arg, function, line);
|
||||
}
|
||||
|
||||
int TestNrSocket::PortMapping::cancel(int how) {
|
||||
r_log(LOG_GENERIC, LOG_DEBUG,
|
||||
"PortMapping %s -> %s stop waiting for %s",
|
||||
external_socket_->my_addr().as_string,
|
||||
remote_address_.as_string,
|
||||
how == NR_ASYNC_WAIT_READ ? "read" : "write");
|
||||
|
||||
return external_socket_->cancel(how);
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/* -*- 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/. */
|
||||
/*
|
||||
*/
|
||||
|
||||
/*
|
||||
Based partially on original code from nICEr and nrappkit.
|
||||
|
||||
nICEr copyright:
|
||||
|
||||
Copyright (c) 2007, Adobe Systems, Incorporated
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Adobe Systems, Network Resonance nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
nrappkit copyright:
|
||||
|
||||
Copyright (C) 2001-2003, Network Resonance, Inc.
|
||||
Copyright (C) 2006, Network Resonance, Inc.
|
||||
All Rights Reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of Network Resonance, Inc. nor the name of any
|
||||
contributors to this software may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
ekr@rtfm.com Thu Dec 20 20:14:49 2001
|
||||
*/
|
||||
|
||||
// Original author: bcampen@mozilla.com [:bwc]
|
||||
|
||||
#ifndef test_nr_socket__
|
||||
#define test_nr_socket__
|
||||
|
||||
#include "nr_socket_prsock.h"
|
||||
|
||||
extern "C" {
|
||||
#include "nr_socket.h"
|
||||
}
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "prinrval.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TestNrSocket;
|
||||
|
||||
/**
|
||||
* A group of TestNrSockets that behave as if they were behind the same NAT.
|
||||
* @note We deliberately avoid addref/release of TestNrSocket here to avoid
|
||||
* masking lifetime errors elsewhere.
|
||||
*/
|
||||
class TestNat {
|
||||
public:
|
||||
typedef enum {
|
||||
/** For mapping, one port is used for all destinations.
|
||||
* For filtering, allow any external address/port. */
|
||||
ENDPOINT_INDEPENDENT,
|
||||
|
||||
/** For mapping, one port for each destination address (for any port).
|
||||
* For filtering, allow incoming traffic from addresses that outgoing
|
||||
* traffic has been sent to. */
|
||||
ADDRESS_DEPENDENT,
|
||||
|
||||
/** For mapping, one port for each destination address/port.
|
||||
* For filtering, allow incoming traffic only from addresses/ports that
|
||||
* outgoing traffic has been sent to. */
|
||||
PORT_DEPENDENT,
|
||||
} NatBehavior;
|
||||
|
||||
TestNat() :
|
||||
enabled_(false),
|
||||
filtering_type_(ENDPOINT_INDEPENDENT),
|
||||
mapping_type_(ENDPOINT_INDEPENDENT),
|
||||
mapping_timeout_(30000),
|
||||
allow_hairpinning_(false),
|
||||
refresh_on_ingress_(false),
|
||||
block_udp_(false),
|
||||
sockets_() {}
|
||||
|
||||
bool has_port_mappings() const;
|
||||
|
||||
// Helps determine whether we're hairpinning
|
||||
bool is_my_external_tuple(const nr_transport_addr &addr) const;
|
||||
bool is_an_internal_tuple(const nr_transport_addr &addr) const;
|
||||
|
||||
int create_socket_factory(nr_socket_factory **factorypp);
|
||||
|
||||
void insert_socket(TestNrSocket *socket) {
|
||||
sockets_.insert(socket);
|
||||
}
|
||||
|
||||
void erase_socket(TestNrSocket *socket) {
|
||||
sockets_.erase(socket);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNat);
|
||||
|
||||
bool enabled_;
|
||||
TestNat::NatBehavior filtering_type_;
|
||||
TestNat::NatBehavior mapping_type_;
|
||||
uint32_t mapping_timeout_;
|
||||
bool allow_hairpinning_;
|
||||
bool refresh_on_ingress_;
|
||||
bool block_udp_;
|
||||
|
||||
private:
|
||||
std::set<TestNrSocket*> sockets_;
|
||||
|
||||
~TestNat(){}
|
||||
};
|
||||
|
||||
/**
|
||||
* Subclass of NrSocket that can simulate things like being behind a NAT, packet
|
||||
* loss, latency, packet rewriting, etc. Also exposes some stuff that assists in
|
||||
* diagnostics.
|
||||
*/
|
||||
class TestNrSocket : public NrSocket {
|
||||
public:
|
||||
explicit TestNrSocket(TestNat *nat);
|
||||
|
||||
virtual ~TestNrSocket();
|
||||
|
||||
bool has_port_mappings() const;
|
||||
bool is_my_external_tuple(const nr_transport_addr &addr) const;
|
||||
|
||||
// Overrides of NrSocket
|
||||
int sendto(const void *msg, size_t len,
|
||||
int flags, nr_transport_addr *to) override;
|
||||
int recvfrom(void * buf, size_t maxlen,
|
||||
size_t *len, int flags,
|
||||
nr_transport_addr *from) override;
|
||||
int connect(nr_transport_addr *addr) override;
|
||||
int write(const void *msg, size_t len, size_t *written) override;
|
||||
int read(void *buf, size_t maxlen, size_t *len) override;
|
||||
|
||||
int async_wait(int how, NR_async_cb cb, void *cb_arg,
|
||||
char *function, int line) override;
|
||||
int cancel(int how) override;
|
||||
|
||||
private:
|
||||
class UdpPacket {
|
||||
public:
|
||||
UdpPacket(const void *msg, size_t len, const nr_transport_addr &addr) :
|
||||
buffer_(new DataBuffer(static_cast<const uint8_t*>(msg), len)) {
|
||||
// TODO(bug 1170299): Remove const_cast when no longer necessary
|
||||
nr_transport_addr_copy(&remote_address_,
|
||||
const_cast<nr_transport_addr*>(&addr));
|
||||
}
|
||||
|
||||
nr_transport_addr remote_address_;
|
||||
UniquePtr<DataBuffer> buffer_;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UdpPacket);
|
||||
private:
|
||||
~UdpPacket(){}
|
||||
};
|
||||
|
||||
class PortMapping {
|
||||
public:
|
||||
PortMapping(const nr_transport_addr &remote_address,
|
||||
const nsRefPtr<NrSocket> &external_socket);
|
||||
|
||||
int sendto(const void *msg, size_t len, const nr_transport_addr &to);
|
||||
int async_wait(int how, NR_async_cb cb, void *cb_arg,
|
||||
char *function, int line);
|
||||
int cancel(int how);
|
||||
int send_from_queue();
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping);
|
||||
|
||||
PRIntervalTime last_used_;
|
||||
nsRefPtr<NrSocket> external_socket_;
|
||||
// For non-symmetric, most of the data here doesn't matter
|
||||
nr_transport_addr remote_address_;
|
||||
|
||||
private:
|
||||
~PortMapping(){}
|
||||
|
||||
// If external_socket_ returns E_WOULDBLOCK, we don't want to propagate
|
||||
// that to the code using the TestNrSocket. We can also perhaps use this
|
||||
// to help simulate things like latency.
|
||||
std::list<nsRefPtr<UdpPacket>> send_queue_;
|
||||
};
|
||||
|
||||
bool is_port_mapping_stale(const PortMapping &port_mapping) const;
|
||||
bool allow_ingress(const nr_transport_addr &from,
|
||||
PortMapping **port_mapping_used) const;
|
||||
void destroy_stale_port_mappings();
|
||||
|
||||
static void port_mapping_readable_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v);
|
||||
void on_port_mapping_readable(NrSocket *external_socket);
|
||||
void fire_readable_callback();
|
||||
|
||||
static void port_mapping_tcp_passthrough_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v);
|
||||
void cancel_port_mapping_async_wait(int how);
|
||||
|
||||
static void port_mapping_writeable_callback(void *ext_sock_v,
|
||||
int how,
|
||||
void *test_sock_v);
|
||||
void write_to_port_mapping(NrSocket *external_socket);
|
||||
bool is_tcp_connection_behind_nat() const;
|
||||
|
||||
PortMapping* get_port_mapping(const nr_transport_addr &remote_addr,
|
||||
TestNat::NatBehavior filter) const;
|
||||
PortMapping* create_port_mapping(
|
||||
const nr_transport_addr &remote_addr,
|
||||
const nsRefPtr<NrSocket> &external_socket) const;
|
||||
nsRefPtr<NrSocket> create_external_socket(
|
||||
const nr_transport_addr &remote_addr) const;
|
||||
|
||||
nsRefPtr<NrSocket> readable_socket_;
|
||||
nsRefPtr<TestNat> nat_;
|
||||
// Since our comparison logic is different depending on what kind of NAT
|
||||
// we simulate, and the STL does not make it very easy to switch out the
|
||||
// comparison function at runtime, and these lists are going to be very
|
||||
// small anyway, we just brute-force it.
|
||||
std::list<nsRefPtr<PortMapping>> port_mappings_;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // test_nr_socket__
|
||||
|
|
@ -196,7 +196,7 @@ static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_compon
|
|||
continue;
|
||||
}
|
||||
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
|
||||
if(r=nr_socket_local_create(&addrs[i].addr,&sock)){
|
||||
if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addrs[i].addr,&sock))){
|
||||
r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
|
||||
continue;
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_compon
|
|||
addr.protocol = IPPROTO_TCP;
|
||||
if ((r=nr_transport_addr_fmt_addr_string(&addr)))
|
||||
ABORT(r);
|
||||
if((r=nr_socket_local_create(&addr, &sock))){
|
||||
if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addr,&sock))){
|
||||
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ static char *RCSSTRING __UNUSED__="$Id: ice_ctx.c,v 1.2 2008/04/28 17:59:01 ekr
|
|||
#include "nr_crypto.h"
|
||||
#include "async_timer.h"
|
||||
#include "util.h"
|
||||
#include "nr_socket_local.h"
|
||||
|
||||
|
||||
int LOG_ICE = 0;
|
||||
|
@ -65,6 +66,14 @@ static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out);
|
|||
#endif /* USE_TURN */
|
||||
static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
|
||||
static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand);
|
||||
static int no_op(void **obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nr_socket_factory_vtbl default_socket_factory_vtbl = {
|
||||
nr_socket_local_create,
|
||||
no_op
|
||||
};
|
||||
|
||||
int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out)
|
||||
{
|
||||
|
@ -229,6 +238,12 @@ int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_fa
|
|||
return(_status);
|
||||
}
|
||||
|
||||
void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory)
|
||||
{
|
||||
nr_socket_factory_destroy(&ctx->socket_factory);
|
||||
ctx->socket_factory = factory;
|
||||
}
|
||||
|
||||
#ifdef USE_TURN
|
||||
int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out)
|
||||
{
|
||||
|
@ -381,6 +396,9 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp)
|
|||
|
||||
ctx->Ta = 20;
|
||||
|
||||
if (r=nr_socket_factory_create_int(NULL, &default_socket_factory_vtbl, &ctx->socket_factory))
|
||||
ABORT(r);
|
||||
|
||||
STAILQ_INIT(&ctx->streams);
|
||||
STAILQ_INIT(&ctx->sockets);
|
||||
STAILQ_INIT(&ctx->foundations);
|
||||
|
@ -439,6 +457,7 @@ static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
|
|||
nr_resolver_destroy(&ctx->resolver);
|
||||
nr_interface_prioritizer_destroy(&ctx->interface_prioritizer);
|
||||
nr_socket_wrapper_factory_destroy(&ctx->turn_tcp_socket_wrapper);
|
||||
nr_socket_factory_destroy(&ctx->socket_factory);
|
||||
|
||||
RFREE(ctx);
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@ struct nr_ice_ctx_ {
|
|||
nr_resolver *resolver; /* The resolver to use */
|
||||
nr_interface_prioritizer *interface_prioritizer; /* Priority decision logic */
|
||||
nr_socket_wrapper_factory *turn_tcp_socket_wrapper; /* The TURN TCP socket wrapper to use */
|
||||
nr_socket_factory *socket_factory;
|
||||
|
||||
nr_ice_foundation_head foundations;
|
||||
|
||||
|
@ -173,6 +174,7 @@ int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int
|
|||
int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver);
|
||||
int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer);
|
||||
int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper);
|
||||
void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory);
|
||||
int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg);
|
||||
|
||||
#define NR_ICE_MAX_ATTRIBUTE_SIZE 256
|
||||
|
|
|
@ -135,3 +135,43 @@ int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen,
|
|||
CHECK_DEFINED(sread);
|
||||
return sock->vtbl->sread(sock->obj, buf, maxlen, len);
|
||||
}
|
||||
|
||||
|
||||
int nr_socket_factory_create_int(void *obj,
|
||||
nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp)
|
||||
{
|
||||
int _status;
|
||||
nr_socket_factory *factoryp=0;
|
||||
|
||||
if(!(factoryp=RCALLOC(sizeof(nr_socket_factory))))
|
||||
ABORT(R_NO_MEMORY);
|
||||
|
||||
factoryp->obj = obj;
|
||||
factoryp->vtbl = vtbl;
|
||||
|
||||
*factorypp = factoryp;
|
||||
|
||||
_status=0;
|
||||
abort:
|
||||
return(_status);
|
||||
}
|
||||
|
||||
int nr_socket_factory_destroy(nr_socket_factory **factorypp)
|
||||
{
|
||||
nr_socket_factory *factoryp;
|
||||
|
||||
if (!factorypp || !*factorypp)
|
||||
return (0);
|
||||
|
||||
factoryp = *factorypp;
|
||||
*factorypp = NULL;
|
||||
factoryp->vtbl->destroy(&factoryp->obj);
|
||||
RFREE(factoryp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp)
|
||||
{
|
||||
return factory->vtbl->create_socket(factory->obj, addr, sockp);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,15 @@ typedef struct nr_socket_ {
|
|||
nr_socket_vtbl *vtbl;
|
||||
} nr_socket;
|
||||
|
||||
typedef struct nr_socket_factory_vtbl_ {
|
||||
int (*create_socket)(void *obj, nr_transport_addr *addr, nr_socket **sockp);
|
||||
int (*destroy)(void **obj);
|
||||
} nr_socket_factory_vtbl;
|
||||
|
||||
typedef struct nr_socket_factory_ {
|
||||
void *obj;
|
||||
nr_socket_factory_vtbl *vtbl;
|
||||
} nr_socket_factory;
|
||||
|
||||
/* To be called by constructors */
|
||||
int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp);
|
||||
|
@ -87,5 +96,9 @@ int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr);
|
|||
int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags);
|
||||
int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags);
|
||||
|
||||
int nr_socket_factory_create_int(void *obj, nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp);
|
||||
int nr_socket_factory_destroy(nr_socket_factory **factoryp);
|
||||
int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef _nr_socket_local_h
|
||||
#define _nr_socket_local_h
|
||||
|
||||
int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp);
|
||||
int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ skip-if = os == 'b2g' #Bug 919595
|
|||
[TestWebGLElementArrayCache]
|
||||
[buffered_stun_socket_unittest]
|
||||
[ice_unittest]
|
||||
[test_nr_socket_unittest]
|
||||
[jsapi-tests]
|
||||
skip-if = os == 'b2g' #Bug 1068946
|
||||
[mediaconduit_unittests]
|
||||
|
|
Загрузка…
Ссылка в новой задаче