Files
palemoon27/media/mtransport/test_nr_socket.cpp
T
roytam1 b383d6bc2f import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1161101 - Use JS_GetOwnPropertyDescriptor instead of GetPropertyDescriptor in dom/bindings. r=bz (918d7961c)
- Bug 1119387 part 5: Update docker images. (0d47fde65)
- Bug 1144463 - Add dolphin-512 and update docker. r=jlal,wcosta (aa6a55cab)
- Bug 1134226: Add lightsaber nightly builds. (7926eff96)
- Bug 1151981 - Properly check for MSVC (mingw fixup). (d95e17266)
- Bug 1154947 part 3: Add aries nightly user, userdebug and eng builds. (45e096ed6)
- Bug 1178932 - Implement Reflect.construct. r=Waldo. (4c5f0e72e)
- Bug 1131206 - Remove the less useful commands from taskcluster mach r=garndt (e4c8ed78a)
- Bug 1131206 - Select mozharness version from in tree r=garndt (fdab5140d)
- bug 1156816 - Fix scopes for aws-provisioner. Switch nightly builds to production balrog. r=garndt (561d5cd58)
- Bug 1166073 - change docker registry to hub r=garndt (17faea355)
- Bug 1166233: Bring taskcluster-build mach command back. (c0a719c6b)
- Bug 1142779 - Add buildbot steps for cloning and running tests r=lightsofapollo (304235375)
- Bug 1142779 - Update job tasks to include functional unit and dual sim r=lightsofapollo (09b320602)
- Bug 1147605 - Disable tests on phone builds r=me CLOSED TREE (0912de6ba)
- Bug 1142779 - Update job tasks to use new chunking logic r=lightsofapollo (70c531920)
- Bug 1144994 part 1: Update provisioner. (2447affa4)
- Bug 1144994 part 2: Switch aries nightlies from cypress to m-c. (3019a6878)
- Bug 1164939: Provide flame-kk user, userdebug and eng builds. (ab7ad22a7)
- goanna->gecko (70b49bb18)
- Bug 1166745: Reorganize tasks. (d3090e0a9)
- Bug 1170378: Create tasks timestamps in UTC. r=jonasfj a=jonasfj (60160f061)
- Bug 987902 - Add a "doctor" mach command; r=gps (be60e7df6)
- Bug 985857 - Automatically log mach output and add a command to display it. r=gps (90993b77f)
- Bug 991983 - Add a ContextDerivedTypedListWithItems type. r=gps (ad9482c7b)
- Bug 991983 - Don't pass template paths to contexts. r=gps (30a4f2038)
- Bug 991983 - Refactor SourcePath handling for moz.build, add new classes and extensive tests. r=gps (00aeb401b)
- Bug 1142494 - Only package the steeplechase tests if webrtc is enabled. r=ted, f=drno (760943034)
- Bug 1142494 - Fix OSX packaging mistake. r=glandium, a=bustage (03cd3ca35)
- Bug 1147029 - Land luciddream in-tree, r=ted (8a4d2651b)
- Bug 1147031 - Write mach command for luciddream. r=jgriffin (b913c4df4)
- Bug 1171446 - Add a description to the luciddream mach target r=ochameau (c807799b6)
- Bug 1176642 - Remove unused imports; r=glandium (b0e458f5d)
- Bug 1151080: Rewrite NR_async_timer_set(0) to use direct dispatch. r=mt (6c3dda54d)
- Bug 1152185 - Include port/generic/include also for mtransport/test and webrtc/signaling/test. r=ekr (969ce4205)
- Bug 1156621 - Don't assume --without-system-nspr. r=glandium (4cf9c3e76)
- Bug 1035468: A NAT simulator based on NrSocket, and integrate into ice_unittest. r=ekr (903d8f6c8)
- Bug 1162026 - move WrapRunnable &co over to variadic templates; r=ekr (6224de8e9)
- Bug 1163328 - Add a Tuple class to MFBT. r=froydnj (ba3276ef3)
- Bug 1163328 - Add an And<...> class to TemplateLib.h which performs logical and on a variadic number of booleans known at compile time. r=froydnj (ac3afcabd)
- Bug 1175621 - make WrapRunnable* more efficient by utilizing moves in wrapper functions; r=ekr (15cf9f55a)
2020-11-27 09:26:17 +08:00

764 lines
25 KiB
C++

/* -*- 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