Bug 1231971 - Refactor the NAT simulator to use e10s sockets when appropriate. r=drno

This commit is contained in:
Byron Campen [:bwc] 2016-01-20 17:25:26 -06:00
parent b3c66fc709
commit 8c396e68e6
8 changed files with 418 additions and 104 deletions

View File

@ -909,6 +909,8 @@ int NrSocket::listen(int backlog) {
assert(fd_); assert(fd_);
status = PR_Listen(fd_, backlog); status = PR_Listen(fd_, backlog);
if (status != PR_SUCCESS) { if (status != PR_SUCCESS) {
r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d",
__FUNCTION__, PR_GetError());
ABORT(R_IO_ERROR); ABORT(R_IO_ERROR);
} }
@ -2055,24 +2057,26 @@ static nr_socket_vtbl nr_socket_local_vtbl={
nr_socket_local_accept nr_socket_local_accept
}; };
int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) { /* static */
RefPtr<NrSocketBase> sock; int
NrSocketBase::CreateSocket(nr_transport_addr *addr, RefPtr<NrSocketBase> *sock)
{
int r, _status; int r, _status;
// create IPC bridge for content process // create IPC bridge for content process
if (XRE_IsParentProcess()) { if (XRE_IsParentProcess()) {
sock = new NrSocket(); *sock = new NrSocket();
} else { } else {
switch (addr->protocol) { switch (addr->protocol) {
case IPPROTO_UDP: case IPPROTO_UDP:
sock = new NrUdpSocketIpc(); *sock = new NrUdpSocketIpc();
break; break;
case IPPROTO_TCP: case IPPROTO_TCP:
#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API) #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
{ {
nsCOMPtr<nsIThread> main_thread; nsCOMPtr<nsIThread> main_thread;
NS_GetMainThread(getter_AddRefs(main_thread)); NS_GetMainThread(getter_AddRefs(main_thread));
sock = new NrTcpSocketIpc(main_thread.get()); *sock = new NrTcpSocketIpc(main_thread.get());
} }
#else #else
ABORT(R_REJECTED); ABORT(R_REJECTED);
@ -2081,10 +2085,27 @@ int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp
} }
} }
r = sock->create(addr); r = (*sock)->create(addr);
if (r) if (r)
ABORT(r); ABORT(r);
_status = 0;
abort:
if (_status) {
*sock = nullptr;
}
return _status;
}
int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) {
RefPtr<NrSocketBase> sock;
int r, _status;
r = NrSocketBase::CreateSocket(addr, &sock);
if (r) {
ABORT(r);
}
r = nr_socket_create_int(static_cast<void *>(sock), r = nr_socket_create_int(static_cast<void *>(sock),
sock->vtbl(), sockp); sock->vtbl(), sockp);
if (r) if (r)

View File

@ -96,6 +96,10 @@ public:
} }
virtual ~NrSocketBase() {} virtual ~NrSocketBase() {}
// Factory method; will create either an NrSocket, NrUdpSocketIpc, or
// NrTcpSocketIpc as appropriate.
static int CreateSocket(nr_transport_addr *addr, RefPtr<NrSocketBase> *sock);
// the nr_socket APIs // the nr_socket APIs
virtual int create(nr_transport_addr *addr) = 0; virtual int create(nr_transport_addr *addr) = 0;
virtual int sendto(const void *msg, size_t len, virtual int sendto(const void *msg, size_t len,
@ -132,9 +136,10 @@ public:
return my_addr_; return my_addr_;
} }
protected:
void fire_callback(int how); void fire_callback(int how);
protected:
bool connect_invoked_; bool connect_invoked_;
nr_transport_addr my_addr_; nr_transport_addr my_addr_;

View File

@ -2098,7 +2098,7 @@ TEST_F(IceGatherTest, TestFakeStunServerNoNatDefaultRouteOnly) {
TEST_F(IceGatherTest, TestStunTcpServerTrickle) { TEST_F(IceGatherTest, TestStunTcpServerTrickle) {
UseFakeStunTcpServerWithResponse("192.0.3.1", 3333); UseFakeStunTcpServerWithResponse("192.0.3.1", 3333);
TestStunServer::GetInstance(AF_INET)->SetDelay(500); TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500);
Gather(0); Gather(0);
ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype ")); ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
WaitForGather(); WaitForGather();

View File

@ -33,6 +33,8 @@ extern "C" {
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "gtest_utils.h" #include "gtest_utils.h"
#define DATA_BUF_SIZE 1024
namespace mozilla { namespace mozilla {
class TestNrSocketTest : public ::testing::Test { class TestNrSocketTest : public ::testing::Test {
@ -62,12 +64,13 @@ class TestNrSocketTest : public ::testing::Test {
} }
RefPtr<TestNrSocket> CreateTestNrSocket_s(const char *ip_str, RefPtr<TestNrSocket> CreateTestNrSocket_s(const char *ip_str,
TestNat *nat) { int proto,
TestNat *nat) {
// If no nat is supplied, we create a default NAT which is disabled. This // If no nat is supplied, we create a default NAT which is disabled. This
// is how we simulate a non-natted socket. // is how we simulate a non-natted socket.
RefPtr<TestNrSocket> sock(new TestNrSocket(nat ? nat : new TestNat)); RefPtr<TestNrSocket> sock(new TestNrSocket(nat ? nat : new TestNat));
nr_transport_addr address; nr_transport_addr address;
nr_str_port_to_transport_addr(ip_str, 0, IPPROTO_UDP, &address); nr_str_port_to_transport_addr(ip_str, 0, proto, &address);
int r = sock->create(&address); int r = sock->create(&address);
if (r) { if (r) {
return nullptr; return nullptr;
@ -75,40 +78,47 @@ class TestNrSocketTest : public ::testing::Test {
return sock; return sock;
} }
void CreatePublicAddrs(size_t count, const char *ip_str = "127.0.0.1") { void CreatePublicAddrs(size_t count,
const char *ip_str = "127.0.0.1",
int proto = IPPROTO_UDP) {
sts_->Dispatch( sts_->Dispatch(
WrapRunnable(this, WrapRunnable(this,
&TestNrSocketTest::CreatePublicAddrs_s, &TestNrSocketTest::CreatePublicAddrs_s,
count, count,
ip_str), ip_str,
proto),
NS_DISPATCH_SYNC); NS_DISPATCH_SYNC);
} }
void CreatePublicAddrs_s(size_t count, const char* ip_str) { void CreatePublicAddrs_s(size_t count, const char* ip_str, int proto) {
while (count--) { while (count--) {
auto sock = CreateTestNrSocket_s(ip_str, nullptr); auto sock = CreateTestNrSocket_s(ip_str, proto, nullptr);
ASSERT_TRUE(sock) << "Failed to create socket"; ASSERT_TRUE(sock) << "Failed to create socket";
public_addrs_.push_back(sock); public_addrs_.push_back(sock);
} }
} }
RefPtr<TestNat> CreatePrivateAddrs(size_t size, RefPtr<TestNat> CreatePrivateAddrs(size_t size,
const char* ip_str = "127.0.0.1") { const char* ip_str = "127.0.0.1",
int proto = IPPROTO_UDP) {
RefPtr<TestNat> result; RefPtr<TestNat> result;
sts_->Dispatch( sts_->Dispatch(
WrapRunnableRet(&result, WrapRunnableRet(&result,
this, this,
&TestNrSocketTest::CreatePrivateAddrs_s, &TestNrSocketTest::CreatePrivateAddrs_s,
size, size,
ip_str), ip_str,
proto),
NS_DISPATCH_SYNC); NS_DISPATCH_SYNC);
return result; return result;
} }
RefPtr<TestNat> CreatePrivateAddrs_s(size_t count, const char* ip_str) { RefPtr<TestNat> CreatePrivateAddrs_s(size_t count,
const char* ip_str,
int proto) {
RefPtr<TestNat> nat(new TestNat); RefPtr<TestNat> nat(new TestNat);
while (count--) { while (count--) {
auto sock = CreateTestNrSocket_s(ip_str, nat); auto sock = CreateTestNrSocket_s(ip_str, proto, nat);
if (!sock) { if (!sock) {
EXPECT_TRUE(false) << "Failed to create socket"; EXPECT_TRUE(false) << "Failed to create socket";
break; break;
@ -178,6 +188,63 @@ class TestNrSocketTest : public ::testing::Test {
sender_external_address); sender_external_address);
} }
bool CheckTcpConnectivity(TestNrSocket *from, TestNrSocket *to) {
NrSocketBase *accepted_sock;
if (!Connect(from, to, &accepted_sock)) {
std::cerr << "Connect failed" << std::endl;
return false;
}
// write on |from|, recv on |accepted_sock|
if (!WaitForWriteable(from)) {
std::cerr << __LINE__ << "WaitForWriteable failed" << std::endl;
return false;
}
int r;
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::SendDataTcp_s,
from),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "SendDataTcp_s (1) failed" << std::endl;
return false;
}
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::RecvDataTcp_s,
accepted_sock),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "RecvDataTcp_s (1) failed" << std::endl;
return false;
}
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::SendDataTcp_s,
accepted_sock),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "SendDataTcp_s (2) failed" << std::endl;
return false;
}
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::RecvDataTcp_s,
from),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "RecvDataTcp_s (2) failed" << std::endl;
return false;
}
return true;
}
int GetAddress(TestNrSocket *sock, nr_transport_addr_ *address) { int GetAddress(TestNrSocket *sock, nr_transport_addr_ *address) {
MOZ_ASSERT(sock); MOZ_ASSERT(sock);
MOZ_ASSERT(address); MOZ_ASSERT(address);
@ -203,10 +270,16 @@ class TestNrSocketTest : public ::testing::Test {
const_cast<nr_transport_addr*>(&to)); const_cast<nr_transport_addr*>(&to));
} }
int SendDataTcp_s(NrSocketBase *from) {
// It is up to caller to ensure that |from| is writeable.
const char buf[] = "foobajooba";
size_t written;
return from->write(buf, sizeof(buf), &written);
}
int RecvData_s(TestNrSocket *to, nr_transport_addr *from) { int RecvData_s(TestNrSocket *to, nr_transport_addr *from) {
// It is up to caller to ensure that |to| is readable // It is up to caller to ensure that |to| is readable
const size_t bufSize = 1024; char buf[DATA_BUF_SIZE];
char buf[bufSize];
size_t len; size_t len;
// Maybe check that data matches? // Maybe check that data matches?
int r = to->recvfrom(buf, sizeof(buf), &len, 0, from); int r = to->recvfrom(buf, sizeof(buf), &len, 0, from);
@ -216,6 +289,100 @@ class TestNrSocketTest : public ::testing::Test {
return r; return r;
} }
int RecvDataTcp_s(NrSocketBase *to) {
// It is up to caller to ensure that |to| is readable
char buf[DATA_BUF_SIZE];
size_t len;
// Maybe check that data matches?
int r = to->read(buf, sizeof(buf), &len);
if (!r && (len == 0)) {
r = R_INTERNAL;
}
return r;
}
int Listen_s(TestNrSocket *to) {
// listen on |to|
int r = to->listen(1);
if (r) {
return r;
}
return 0;
}
int Connect_s(TestNrSocket *from, TestNrSocket *to) {
// connect on |from|
nr_transport_addr destination_address;
int r = to->getaddr(&destination_address);
if (r) {
return r;
}
r = from->connect(&destination_address);
if (r) {
return r;
}
return 0;
}
int Accept_s(TestNrSocket *to, NrSocketBase **accepted_sock) {
nr_socket *sock;
nr_transport_addr source_address;
int r = to->accept(&source_address, &sock);
if (r) {
return r;
}
*accepted_sock = reinterpret_cast<NrSocketBase*>(sock->obj);
return 0;
}
bool Connect(TestNrSocket *from,
TestNrSocket *to,
NrSocketBase **accepted_sock) {
int r;
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::Listen_s,
to),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "Listen_s failed: " << r << std::endl;
return false;
}
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::Connect_s,
from,
to),
NS_DISPATCH_SYNC);
if (r && r != R_WOULDBLOCK) {
std::cerr << "Connect_s failed: " << r << std::endl;
return false;
}
if (!WaitForReadable(to)) {
std::cerr << "WaitForReadable failed" << std::endl;
return false;
}
sts_->Dispatch(WrapRunnableRet(&r,
this,
&TestNrSocketTest::Accept_s,
to,
accepted_sock),
NS_DISPATCH_SYNC);
if (r) {
std::cerr << "Accept_s failed: " << r << std::endl;
return false;
}
return true;
}
bool WaitForSocketState(TestNrSocket *sock, int state) { bool WaitForSocketState(TestNrSocket *sock, int state) {
MOZ_ASSERT(sock); MOZ_ASSERT(sock);
sts_->Dispatch(WrapRunnable(this, sts_->Dispatch(WrapRunnable(this,
@ -641,8 +808,52 @@ TEST_F(TestNrSocketTest, FullConeTimeout) {
sender_external_address)); sender_external_address));
} }
// TODO(): We need TCP tests, but first we will need ICE TCP to land (this TEST_F(TestNrSocketTest, PublicConnectivityTcp)
// adds listen/accept support to NrSocket) {
CreatePublicAddrs(2, "127.0.0.1", IPPROTO_TCP);
ASSERT_TRUE(CheckTcpConnectivity(public_addrs_[0], public_addrs_[1]));
}
TEST_F(TestNrSocketTest, PrivateConnectivityTcp) {
RefPtr<TestNat> nat(CreatePrivateAddrs(2, "127.0.0.1", IPPROTO_TCP));
nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1]));
}
TEST_F(TestNrSocketTest, PrivateToPublicConnectivityTcp)
{
RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP);
ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], public_addrs_[0]));
}
TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnetsTcp)
{
RefPtr<TestNat> nat1(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
RefPtr<TestNat> nat2(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
ASSERT_FALSE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1]));
}
TEST_F(TestNrSocketTest, NoConnectivityPublicToPrivateTcp)
{
RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP);
ASSERT_FALSE(CheckTcpConnectivity(public_addrs_[0], private_addrs_[0]));
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {

View File

@ -192,7 +192,7 @@ TestNrSocket::~TestNrSocket() {
nat_->erase_socket(this); nat_->erase_socket(this);
} }
RefPtr<NrSocket> TestNrSocket::create_external_socket( RefPtr<NrSocketBase> TestNrSocket::create_external_socket(
const nr_transport_addr &dest_addr) const { const nr_transport_addr &dest_addr) const {
MOZ_ASSERT(nat_->enabled_); MOZ_ASSERT(nat_->enabled_);
MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr)); MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
@ -202,8 +202,9 @@ RefPtr<NrSocket> TestNrSocket::create_external_socket(
// Open the socket on an arbitrary port, on the same address. // Open the socket on an arbitrary port, on the same address.
// TODO(bug 1170299): Remove const_cast when no longer necessary // TODO(bug 1170299): Remove const_cast when no longer necessary
if ((r = nr_transport_addr_copy(&nat_external_addr, if ((r = nr_transport_addr_copy(
const_cast<nr_transport_addr*>(&my_addr_)))) { &nat_external_addr,
const_cast<nr_transport_addr*>(&internal_socket_->my_addr())))) {
r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d", r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
__FUNCTION__, r); __FUNCTION__, r);
return nullptr; return nullptr;
@ -215,9 +216,10 @@ RefPtr<NrSocket> TestNrSocket::create_external_socket(
return nullptr; return nullptr;
} }
RefPtr<NrSocket> external_socket = new NrSocket; RefPtr<NrSocketBase> external_socket;
r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket);
if ((r = external_socket->create(&nat_external_addr))) { if (r) {
r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d", r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d",
__FUNCTION__, r); __FUNCTION__, r);
return nullptr; return nullptr;
@ -226,13 +228,49 @@ RefPtr<NrSocket> TestNrSocket::create_external_socket(
return external_socket; return external_socket;
} }
int TestNrSocket::create(nr_transport_addr *addr) {
return NrSocketBase::CreateSocket(addr, &internal_socket_);
}
int TestNrSocket::getaddr(nr_transport_addr *addrp) {
return internal_socket_->getaddr(addrp);
}
void TestNrSocket::close() {
// TODO: close port mappings too?
internal_socket_->close();
}
int TestNrSocket::listen(int backlog) {
MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
r_log(LOG_GENERIC, LOG_DEBUG,
"TestNrSocket %s listening",
internal_socket_->my_addr().as_string);
return internal_socket_->listen(backlog);
}
int TestNrSocket::accept(nr_transport_addr *addrp, nr_socket **sockp) {
MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
int r = internal_socket_->accept(addrp, sockp);
if (r) {
return r;
}
if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) {
nr_socket_destroy(sockp);
return R_IO_ERROR;
}
return 0;
}
int TestNrSocket::sendto(const void *msg, size_t len, int TestNrSocket::sendto(const void *msg, size_t len,
int flags, nr_transport_addr *to) { int flags, nr_transport_addr *to) {
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP); MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
ASSERT_ON_THREAD(ststhread_);
if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) { if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
return NrSocket::sendto(msg, len, flags, to); return internal_socket_->sendto(msg, len, flags, to);
} }
destroy_stale_port_mappings(); destroy_stale_port_mappings();
@ -251,7 +289,7 @@ int TestNrSocket::sendto(const void *msg, size_t len,
// See if we have already made the external socket we need to use. // See if we have already made the external socket we need to use.
PortMapping *similar_port_mapping = PortMapping *similar_port_mapping =
get_port_mapping(*to, nat_->mapping_type_); get_port_mapping(*to, nat_->mapping_type_);
RefPtr<NrSocket> external_socket; RefPtr<NrSocketBase> external_socket;
if (similar_port_mapping) { if (similar_port_mapping) {
external_socket = similar_port_mapping->external_socket_; external_socket = similar_port_mapping->external_socket_;
@ -270,7 +308,7 @@ int TestNrSocket::sendto(const void *msg, size_t len,
// Make sure the new port mapping is ready to receive traffic if the // Make sure the new port mapping is ready to receive traffic if the
// TestNrSocket is already waiting. // TestNrSocket is already waiting.
port_mapping->async_wait(NR_ASYNC_WAIT_READ, port_mapping->async_wait(NR_ASYNC_WAIT_READ,
port_mapping_readable_callback, socket_readable_callback,
this, this,
(char*)__FUNCTION__, (char*)__FUNCTION__,
__LINE__); __LINE__);
@ -285,8 +323,7 @@ int TestNrSocket::sendto(const void *msg, size_t len,
int TestNrSocket::recvfrom(void *buf, size_t maxlen, int TestNrSocket::recvfrom(void *buf, size_t maxlen,
size_t *len, int flags, size_t *len, int flags,
nr_transport_addr *from) { nr_transport_addr *from) {
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP); MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
ASSERT_ON_THREAD(ststhread_);
int r; int r;
bool ingress_allowed = false; bool ingress_allowed = false;
@ -306,7 +343,7 @@ int TestNrSocket::recvfrom(void *buf, size_t maxlen,
// If no external socket has data, see if there's any data that was sent // 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 // directly to the TestNrSocket, and eat it if it isn't supposed to get
// through. // through.
r = NrSocket::recvfrom(buf, maxlen, len, flags, from); r = internal_socket_->recvfrom(buf, maxlen, len, flags, from);
if (!r) { if (!r) {
// We do not use allow_ingress() here because that only handles traffic // We do not use allow_ingress() here because that only handles traffic
// landing on an external port. // landing on an external port.
@ -315,7 +352,7 @@ int TestNrSocket::recvfrom(void *buf, size_t maxlen,
if (!ingress_allowed) { if (!ingress_allowed) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: " r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Not behind the same NAT", "Not behind the same NAT",
my_addr_.as_string, internal_socket_->my_addr().as_string,
from->as_string); from->as_string);
} }
} }
@ -348,7 +385,7 @@ bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
if (!(*port_mapping_used)) { if (!(*port_mapping_used)) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: " r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Filtered", "Filtered",
my_addr_.as_string, internal_socket_->my_addr().as_string,
from.as_string); from.as_string);
return false; return false;
} }
@ -356,7 +393,7 @@ bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
if (is_port_mapping_stale(**port_mapping_used)) { if (is_port_mapping_stale(**port_mapping_used)) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: " r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Stale port mapping", "Stale port mapping",
my_addr_.as_string, internal_socket_->my_addr().as_string,
from.as_string); from.as_string);
return false; return false;
} }
@ -364,7 +401,7 @@ bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) { if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: " r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Hairpinning disallowed", "Hairpinning disallowed",
my_addr_.as_string, internal_socket_->my_addr().as_string,
from.as_string); from.as_string);
return false; return false;
} }
@ -373,7 +410,6 @@ bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
} }
int TestNrSocket::connect(nr_transport_addr *addr) { int TestNrSocket::connect(nr_transport_addr *addr) {
ASSERT_ON_THREAD(ststhread_);
if (connect_invoked_ || !port_mappings_.empty()) { if (connect_invoked_ || !port_mappings_.empty()) {
MOZ_CRASH("TestNrSocket::connect() called more than once!"); MOZ_CRASH("TestNrSocket::connect() called more than once!");
@ -386,17 +422,21 @@ int TestNrSocket::connect(nr_transport_addr *addr) {
// we don't normally connect on UDP. // we don't normally connect on UDP.
|| nat_->is_an_internal_tuple(*addr)) { || nat_->is_an_internal_tuple(*addr)) {
// This will set connect_invoked_ // This will set connect_invoked_
return NrSocket::connect(addr); return internal_socket_->connect(addr);
} }
RefPtr<NrSocket> external_socket(create_external_socket(*addr)); RefPtr<NrSocketBase> external_socket(create_external_socket(*addr));
if (!external_socket) { if (!external_socket) {
return R_INTERNAL; return R_INTERNAL;
} }
PortMapping *port_mapping = create_port_mapping(*addr, external_socket); PortMapping *port_mapping = create_port_mapping(*addr, external_socket);
port_mappings_.push_back(port_mapping); port_mappings_.push_back(port_mapping);
port_mapping->external_socket_->connect(addr); int r = port_mapping->external_socket_->connect(addr);
if (r && r != R_WOULDBLOCK) {
return r;
}
port_mapping->last_used_ = PR_IntervalNow(); port_mapping->last_used_ = PR_IntervalNow();
if (poll_flags() & PR_POLL_READ) { if (poll_flags() & PR_POLL_READ) {
@ -407,19 +447,22 @@ int TestNrSocket::connect(nr_transport_addr *addr) {
__LINE__); __LINE__);
} }
return 0; return r;
} }
int TestNrSocket::write(const void *msg, size_t len, size_t *written) { int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
ASSERT_ON_THREAD(ststhread_);
if (port_mappings_.empty()) { if (port_mappings_.empty()) {
// The no-nat case, just pass call through. // The no-nat case, just pass call through.
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s writing", r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s writing",
my_addr().as_string); my_addr().as_string);
return NrSocket::write(msg, len, written); return internal_socket_->write(msg, len, written);
} else { } else {
destroy_stale_port_mappings();
if (port_mappings_.empty()) {
return -1;
}
// This is TCP only // This is TCP only
MOZ_ASSERT(port_mappings_.size() == 1); MOZ_ASSERT(port_mappings_.size() == 1);
r_log(LOG_GENERIC, LOG_INFO, r_log(LOG_GENERIC, LOG_INFO,
@ -432,10 +475,9 @@ int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
} }
int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) { int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
ASSERT_ON_THREAD(ststhread_);
if (port_mappings_.empty()) { if (port_mappings_.empty()) {
return NrSocket::read(buf, maxlen, len); return internal_socket_->read(buf, maxlen, len);
} else { } else {
MOZ_ASSERT(port_mappings_.size() == 1); MOZ_ASSERT(port_mappings_.size() == 1);
return port_mappings_.front()->external_socket_->read(buf, maxlen, len); return port_mappings_.front()->external_socket_->read(buf, maxlen, len);
@ -444,25 +486,41 @@ int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg, int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
char *function, int line) { char *function, int line) {
ASSERT_ON_THREAD(ststhread_); r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
internal_socket_->my_addr().as_string,
how == NR_ASYNC_WAIT_READ ? "read" : "write");
// Make sure we're waiting on the socket for the internal address int r;
int r = NrSocket::async_wait(how, cb, cb_arg, function, line);
if (how == NR_ASYNC_WAIT_READ) {
NrSocketBase::async_wait(how, cb, cb_arg, function, line);
// Make sure we're waiting on the socket for the internal address
r = internal_socket_->async_wait(how,
socket_readable_callback,
this,
function,
line);
} else {
// For write, just use the readiness of the internal socket, since we queue
// everything for the port mappings.
r = internal_socket_->async_wait(how,
cb,
cb_arg,
function,
line);
}
if (r) { if (r) {
return 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()) { if (is_tcp_connection_behind_nat()) {
// Bypass all port-mapping related logic // Bypass all port-mapping related logic
return 0; return 0;
} }
if (my_addr_.protocol == IPPROTO_TCP) { if (internal_socket_->my_addr().protocol == IPPROTO_TCP) {
// For a TCP connection through a simulated NAT, these signals are // For a TCP connection through a simulated NAT, these signals are
// just passed through. // just passed through.
MOZ_ASSERT(port_mappings_.size() == 1); MOZ_ASSERT(port_mappings_.size() == 1);
@ -478,7 +536,7 @@ int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
for (PortMapping *port_mapping : port_mappings_) { for (PortMapping *port_mapping : port_mappings_) {
// Be ready to receive traffic on our port mappings // Be ready to receive traffic on our port mappings
r = port_mapping->async_wait(how, r = port_mapping->async_wait(how,
port_mapping_readable_callback, socket_readable_callback,
this, this,
function, function,
line); line);
@ -498,18 +556,18 @@ void TestNrSocket::cancel_port_mapping_async_wait(int how) {
} }
int TestNrSocket::cancel(int how) { int TestNrSocket::cancel(int how) {
ASSERT_ON_THREAD(ststhread_);
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s", r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
my_addr_.as_string, internal_socket_->my_addr().as_string,
how == NR_ASYNC_WAIT_READ ? "read" : "write"); how == NR_ASYNC_WAIT_READ ? "read" : "write");
// Writable callbacks are decoupled except for the TCP case // Writable callbacks are decoupled except for the TCP case
if (how == NR_ASYNC_WAIT_READ || my_addr_.protocol == IPPROTO_TCP) { if (how == NR_ASYNC_WAIT_READ ||
internal_socket_->my_addr().protocol == IPPROTO_TCP) {
cancel_port_mapping_async_wait(how); cancel_port_mapping_async_wait(how);
} }
return NrSocket::cancel(how); return internal_socket_->cancel(how);
} }
bool TestNrSocket::has_port_mappings() const { bool TestNrSocket::has_port_mappings() const {
@ -548,7 +606,7 @@ void TestNrSocket::destroy_stale_port_mappings() {
if (is_port_mapping_stale(**temp)) { if (is_port_mapping_stale(**temp)) {
r_log(LOG_GENERIC, LOG_INFO, r_log(LOG_GENERIC, LOG_INFO,
"TestNrSocket %s destroying port mapping %s -> %s", "TestNrSocket %s destroying port mapping %s -> %s",
my_addr_.as_string, internal_socket_->my_addr().as_string,
(*temp)->external_socket_->my_addr().as_string, (*temp)->external_socket_->my_addr().as_string,
(*temp)->remote_address_.as_string); (*temp)->remote_address_.as_string);
@ -557,34 +615,31 @@ void TestNrSocket::destroy_stale_port_mappings() {
} }
} }
void TestNrSocket::port_mapping_readable_callback(void *ext_sock_v, void TestNrSocket::socket_readable_callback(void *real_sock_v,
int how, int how,
void *test_sock_v) { void *test_sock_v) {
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v); TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
NrSocket *external_socket = static_cast<NrSocket*>(ext_sock_v); NrSocket *real_socket = static_cast<NrSocket*>(real_sock_v);
test_socket->on_port_mapping_readable(external_socket); test_socket->on_socket_readable(real_socket);
} }
void TestNrSocket::on_port_mapping_readable(NrSocket *external_socket) { void TestNrSocket::on_socket_readable(NrSocketBase *real_socket) {
if (!readable_socket_) { if (!readable_socket_ && (real_socket != internal_socket_)) {
readable_socket_ = external_socket; readable_socket_ = real_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(); fire_readable_callback();
} }
void TestNrSocket::fire_readable_callback() { void TestNrSocket::fire_readable_callback() {
MOZ_ASSERT(poll_flags() & PR_POLL_READ); MOZ_ASSERT(poll_flags() & PR_POLL_READ);
// Stop listening on all mapped sockets; we will start listening again // Stop listening on all real sockets; we will start listening again
// if the app starts listening to us again. // if the app starts listening to us again.
cancel_port_mapping_async_wait(NR_ASYNC_WAIT_READ); cancel_port_mapping_async_wait(NR_ASYNC_WAIT_READ);
internal_socket_->cancel(NR_ASYNC_WAIT_READ);
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read", r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
my_addr_.as_string); internal_socket_->my_addr().as_string);
fire_callback(NR_ASYNC_WAIT_READ); fire_callback(NR_ASYNC_WAIT_READ);
} }
@ -592,13 +647,13 @@ void TestNrSocket::port_mapping_writeable_callback(void *ext_sock_v,
int how, int how,
void *test_sock_v) { void *test_sock_v) {
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v); TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
NrSocket *external_socket = static_cast<NrSocket*>(ext_sock_v); NrSocketBase *external_socket = static_cast<NrSocketBase*>(ext_sock_v);
test_socket->write_to_port_mapping(external_socket); test_socket->write_to_port_mapping(external_socket);
} }
void TestNrSocket::write_to_port_mapping(NrSocket *external_socket) { void TestNrSocket::write_to_port_mapping(NrSocketBase *external_socket) {
MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP); MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
int r = 0; int r = 0;
for (PortMapping *port_mapping : port_mappings_) { for (PortMapping *port_mapping : port_mappings_) {
@ -626,15 +681,16 @@ void TestNrSocket::port_mapping_tcp_passthrough_callback(void *ext_sock_v,
TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v); TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
r_log(LOG_GENERIC, LOG_INFO, r_log(LOG_GENERIC, LOG_INFO,
"TestNrSocket %s firing %s callback", "TestNrSocket %s firing %s callback",
test_socket->my_addr().as_string, test_socket->internal_socket_->my_addr().as_string,
how == NR_ASYNC_WAIT_READ ? "readable" : "writeable"); how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
test_socket->fire_callback(how); test_socket->internal_socket_->fire_callback(how);
} }
bool TestNrSocket::is_tcp_connection_behind_nat() const { bool TestNrSocket::is_tcp_connection_behind_nat() const {
return my_addr_.protocol == IPPROTO_TCP && port_mappings_.empty(); return internal_socket_->my_addr().protocol == IPPROTO_TCP &&
port_mappings_.empty();
} }
TestNrSocket::PortMapping* TestNrSocket::get_port_mapping( TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
@ -665,9 +721,9 @@ TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
TestNrSocket::PortMapping* TestNrSocket::create_port_mapping( TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
const nr_transport_addr &remote_address, const nr_transport_addr &remote_address,
const RefPtr<NrSocket> &external_socket) const { const RefPtr<NrSocketBase> &external_socket) const {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s", r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
my_addr_.as_string, internal_socket_->my_addr().as_string,
external_socket->my_addr().as_string, external_socket->my_addr().as_string,
remote_address.as_string); remote_address.as_string);
@ -676,7 +732,7 @@ TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
TestNrSocket::PortMapping::PortMapping( TestNrSocket::PortMapping::PortMapping(
const nr_transport_addr &remote_address, const nr_transport_addr &remote_address,
const RefPtr<NrSocket> &external_socket) : const RefPtr<NrSocketBase> &external_socket) :
external_socket_(external_socket) { external_socket_(external_socket) {
// TODO(bug 1170299): Remove const_cast when no longer necessary // TODO(bug 1170299): Remove const_cast when no longer necessary
nr_transport_addr_copy(&remote_address_, nr_transport_addr_copy(&remote_address_,

View File

@ -167,34 +167,45 @@ class TestNat {
}; };
/** /**
* Subclass of NrSocket that can simulate things like being behind a NAT, packet * Subclass of NrSocketBase that can simulate things like being behind a NAT,
* loss, latency, packet rewriting, etc. Also exposes some stuff that assists in * packet loss, latency, packet rewriting, etc. Also exposes some stuff that
* diagnostics. * assists in diagnostics.
* This is accomplished by wrapping an "internal" socket (that handles traffic
* behind the NAT), and a collection of "external" sockets (that handle traffic
* into/out of the NAT)
*/ */
class TestNrSocket : public NrSocket { class TestNrSocket : public NrSocketBase {
public: public:
explicit TestNrSocket(TestNat *nat); explicit TestNrSocket(TestNat *nat);
virtual ~TestNrSocket();
bool has_port_mappings() const; bool has_port_mappings() const;
bool is_my_external_tuple(const nr_transport_addr &addr) const; bool is_my_external_tuple(const nr_transport_addr &addr) const;
// Overrides of NrSocket // Overrides of NrSocketBase
int create(nr_transport_addr *addr) override;
int sendto(const void *msg, size_t len, int sendto(const void *msg, size_t len,
int flags, nr_transport_addr *to) override; int flags, nr_transport_addr *to) override;
int recvfrom(void * buf, size_t maxlen, int recvfrom(void * buf, size_t maxlen,
size_t *len, int flags, size_t *len, int flags,
nr_transport_addr *from) override; nr_transport_addr *from) override;
int getaddr(nr_transport_addr *addrp) override;
void close() override;
int connect(nr_transport_addr *addr) override; int connect(nr_transport_addr *addr) override;
int write(const void *msg, size_t len, size_t *written) 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 read(void *buf, size_t maxlen, size_t *len) override;
int listen(int backlog) override;
int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
int async_wait(int how, NR_async_cb cb, void *cb_arg, int async_wait(int how, NR_async_cb cb, void *cb_arg,
char *function, int line) override; char *function, int line) override;
int cancel(int how) override; int cancel(int how) override;
// Need override since this is virtual in NrSocketBase
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNrSocket, override)
private: private:
virtual ~TestNrSocket();
class UdpPacket { class UdpPacket {
public: public:
UdpPacket(const void *msg, size_t len, const nr_transport_addr &addr) : UdpPacket(const void *msg, size_t len, const nr_transport_addr &addr) :
@ -215,7 +226,7 @@ class TestNrSocket : public NrSocket {
class PortMapping { class PortMapping {
public: public:
PortMapping(const nr_transport_addr &remote_address, PortMapping(const nr_transport_addr &remote_address,
const RefPtr<NrSocket> &external_socket); const RefPtr<NrSocketBase> &external_socket);
int sendto(const void *msg, size_t len, const nr_transport_addr &to); 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, int async_wait(int how, NR_async_cb cb, void *cb_arg,
@ -225,7 +236,7 @@ class TestNrSocket : public NrSocket {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping);
PRIntervalTime last_used_; PRIntervalTime last_used_;
RefPtr<NrSocket> external_socket_; RefPtr<NrSocketBase> external_socket_;
// For non-symmetric, most of the data here doesn't matter // For non-symmetric, most of the data here doesn't matter
nr_transport_addr remote_address_; nr_transport_addr remote_address_;
@ -243,10 +254,10 @@ class TestNrSocket : public NrSocket {
PortMapping **port_mapping_used) const; PortMapping **port_mapping_used) const;
void destroy_stale_port_mappings(); void destroy_stale_port_mappings();
static void port_mapping_readable_callback(void *ext_sock_v, static void socket_readable_callback(void *real_sock_v,
int how, int how,
void *test_sock_v); void *test_sock_v);
void on_port_mapping_readable(NrSocket *external_socket); void on_socket_readable(NrSocketBase *external_or_internal_socket);
void fire_readable_callback(); void fire_readable_callback();
static void port_mapping_tcp_passthrough_callback(void *ext_sock_v, static void port_mapping_tcp_passthrough_callback(void *ext_sock_v,
@ -257,18 +268,21 @@ class TestNrSocket : public NrSocket {
static void port_mapping_writeable_callback(void *ext_sock_v, static void port_mapping_writeable_callback(void *ext_sock_v,
int how, int how,
void *test_sock_v); void *test_sock_v);
void write_to_port_mapping(NrSocket *external_socket); void write_to_port_mapping(NrSocketBase *external_socket);
bool is_tcp_connection_behind_nat() const; bool is_tcp_connection_behind_nat() const;
PortMapping* get_port_mapping(const nr_transport_addr &remote_addr, PortMapping* get_port_mapping(const nr_transport_addr &remote_addr,
TestNat::NatBehavior filter) const; TestNat::NatBehavior filter) const;
PortMapping* create_port_mapping( PortMapping* create_port_mapping(
const nr_transport_addr &remote_addr, const nr_transport_addr &remote_addr,
const RefPtr<NrSocket> &external_socket) const; const RefPtr<NrSocketBase> &external_socket) const;
RefPtr<NrSocket> create_external_socket( RefPtr<NrSocketBase> create_external_socket(
const nr_transport_addr &remote_addr) const; const nr_transport_addr &remote_addr) const;
RefPtr<NrSocket> readable_socket_; RefPtr<NrSocketBase> readable_socket_;
// The socket for the "internal" address; used to talk to stuff behind the
// same nat.
RefPtr<NrSocketBase> internal_socket_;
RefPtr<TestNat> nat_; RefPtr<TestNat> nat_;
// Since our comparison logic is different depending on what kind of 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 // we simulate, and the STL does not make it very easy to switch out the

View File

@ -67,8 +67,7 @@ static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg)
if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){ if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){
if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) { if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) {
/* Report this error upward. Bug 946423 */ /* Report this error upward. Bug 946423 */
r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error %d on reliable socket. Abandoning.",sock->ctx->label, r); r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error %d on reliable socket(%p). Abandoning.",sock->ctx->label, r, s);
NR_ASYNC_CANCEL(s, NR_ASYNC_WAIT_READ);
} }
return; return;
} }

View File

@ -250,7 +250,15 @@ abort:
static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock) static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock)
{ {
NR_SOCKET fd;
sock->read_state = NR_ICE_SOCKET_READ_FAILED; sock->read_state = NR_ICE_SOCKET_READ_FAILED;
/* Cancel waiting on the socket */
if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) {
NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
}
} }
static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf, static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,