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:
Byron Campen [:bwc] 2015-06-10 15:27:12 -07:00
Родитель dc50e12fdd
Коммит dc3971b1c9
17 изменённых файлов: 2042 добавлений и 13 удалений

Просмотреть файл

@ -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]