123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "async_timer.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
- };
- /* static */
- TestNat::NatBehavior
- TestNat::ToNatBehavior(const std::string& type) {
- if (!type.compare("ENDPOINT_INDEPENDENT")) {
- return TestNat::ENDPOINT_INDEPENDENT;
- } else if (!type.compare("ADDRESS_DEPENDENT")) {
- return TestNat::ADDRESS_DEPENDENT;
- } else if (!type.compare("PORT_DEPENDENT")) {
- return TestNat::PORT_DEPENDENT;
- }
- MOZ_ASSERT(false, "Invalid NAT behavior");
- return TestNat::ENDPOINT_INDEPENDENT;
- }
- 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),
- timer_handle_(nullptr) {
- nat_->insert_socket(this);
- }
- TestNrSocket::~TestNrSocket() {
- nat_->erase_socket(this);
- }
- RefPtr<NrSocketBase> 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*>(&internal_socket_->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;
- }
- RefPtr<NrSocketBase> external_socket;
- r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket);
- if (r) {
- r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d",
- __FUNCTION__, r);
- return nullptr;
- }
- 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() {
- if (timer_handle_) {
- NR_async_timer_cancel(timer_handle_);
- timer_handle_ = 0;
- }
- internal_socket_->close();
- for (RefPtr<PortMapping>& port_mapping : port_mappings_) {
- port_mapping->external_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;
- }
- void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void *cb_arg) {
- DeferredPacket *op = static_cast<DeferredPacket *>(cb_arg);
- op->socket_->timer_handle_ = nullptr;
- r_log(LOG_GENERIC, LOG_DEBUG,
- "TestNrSocket %s sending delayed STUN response",
- op->internal_socket_->my_addr().as_string);
- op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(),
- op->flags_, &op->to_);
- delete op;
- }
- int TestNrSocket::sendto(const void *msg, size_t len,
- int flags, nr_transport_addr *to) {
- MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
- UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
- if (nat_->block_stun_ &&
- nr_is_stun_message(buf, len)) {
- return 0;
- }
- /* TODO: improve the functionality of this in bug 1253657 */
- if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
- if (nat_->delay_stun_resp_ms_ &&
- nr_is_stun_response_message(buf, len)) {
- NR_ASYNC_TIMER_SET(nat_->delay_stun_resp_ms_,
- process_delayed_cb,
- new DeferredPacket(this, msg, len, flags, to,
- internal_socket_),
- &timer_handle_);
- return 0;
- }
- return internal_socket_->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_);
- RefPtr<NrSocketBase> 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,
- socket_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(internal_socket_->my_addr().protocol != IPPROTO_TCP);
- 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) {
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s",
- internal_socket_->my_addr().as_string,
- from->as_string,
- port_mapping_used->external_socket_->my_addr().as_string);
- if (nat_->refresh_on_ingress_) {
- 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 = internal_socket_->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",
- internal_socket_->my_addr().as_string,
- from->as_string);
- } else {
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s",
- internal_socket_->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 {
- // This is only called for traffic arriving at a port mapping
- MOZ_ASSERT(nat_->enabled_);
- MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
- *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",
- internal_socket_->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",
- internal_socket_->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",
- internal_socket_->my_addr().as_string,
- from.as_string);
- return false;
- }
- return true;
- }
- int TestNrSocket::connect(nr_transport_addr *addr) {
- if (connect_invoked_ || !port_mappings_.empty()) {
- MOZ_CRASH("TestNrSocket::connect() called more than once!");
- return R_INTERNAL;
- }
- if (!nat_->enabled_
- || addr->protocol==IPPROTO_UDP // Horrible hack to allow default address
- // discovery to work. Only works because
- // we don't normally connect on UDP.
- || nat_->is_an_internal_tuple(*addr)) {
- // This will set connect_invoked_
- return internal_socket_->connect(addr);
- }
- RefPtr<NrSocketBase> 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);
- int r = port_mapping->external_socket_->connect(addr);
- if (r && r != R_WOULDBLOCK) {
- return r;
- }
- 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 r;
- }
- int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
- if (port_mappings_.empty()) {
- // The no-nat case, just pass call through.
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
- my_addr().as_string);
- return internal_socket_->write(msg, len, written);
- } else {
- destroy_stale_port_mappings();
- if (port_mappings_.empty()) {
- return -1;
- }
- // This is TCP only
- MOZ_ASSERT(port_mappings_.size() == 1);
- r_log(LOG_GENERIC, LOG_DEBUG,
- "PortMapping %s -> %s writing",
- port_mappings_.front()->external_socket_->my_addr().as_string,
- port_mappings_.front()->remote_address_.as_string);
- port_mappings_.front()->last_used_ = PR_IntervalNow();
- return port_mappings_.front()->external_socket_->write(msg, len, written);
- }
- }
- int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
- if (port_mappings_.empty()) {
- return internal_socket_->read(buf, maxlen, len);
- } else {
- MOZ_ASSERT(port_mappings_.size() == 1);
- int bytesRead =
- port_mappings_.front()->external_socket_->read(buf, maxlen, len);
- if (bytesRead > 0 && nat_->refresh_on_ingress_) {
- port_mappings_.front()->last_used_ = PR_IntervalNow();
- }
- return bytesRead;
- }
- }
- int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
- char *function, int line) {
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
- internal_socket_->my_addr().as_string,
- how == NR_ASYNC_WAIT_READ ? "read" : "write");
- int r;
- 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) {
- r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
- "internal socket: %d\n",
- internal_socket_->my_addr().as_string,
- r);
- return r;
- }
- if (is_tcp_connection_behind_nat()) {
- // Bypass all port-mapping related logic
- return 0;
- }
- if (internal_socket_->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,
- socket_readable_callback,
- this,
- function,
- line);
- if (r) {
- r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
- "port mapping: %d\n",
- internal_socket_->my_addr().as_string,
- 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) {
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
- internal_socket_->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 ||
- internal_socket_->my_addr().protocol == IPPROTO_TCP) {
- cancel_port_mapping_async_wait(how);
- }
- return internal_socket_->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",
- internal_socket_->my_addr().as_string,
- (*temp)->external_socket_->my_addr().as_string,
- (*temp)->remote_address_.as_string);
- port_mappings_.erase(temp);
- }
- }
- }
- void TestNrSocket::socket_readable_callback(void *real_sock_v,
- int how,
- void *test_sock_v) {
- TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
- NrSocketBase *real_socket = static_cast<NrSocketBase*>(real_sock_v);
- test_socket->on_socket_readable(real_socket);
- }
- void TestNrSocket::on_socket_readable(NrSocketBase *real_socket) {
- if (!readable_socket_ && (real_socket != internal_socket_)) {
- readable_socket_ = real_socket;
- }
- fire_readable_callback();
- }
- void TestNrSocket::fire_readable_callback() {
- MOZ_ASSERT(poll_flags() & PR_POLL_READ);
- r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
- internal_socket_->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);
- NrSocketBase *external_socket = static_cast<NrSocketBase*>(ext_sock_v);
- test_socket->write_to_port_mapping(external_socket);
- }
- void TestNrSocket::write_to_port_mapping(NrSocketBase *external_socket) {
- MOZ_ASSERT(internal_socket_->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_DEBUG,
- "TestNrSocket %s firing %s callback",
- test_socket->internal_socket_->my_addr().as_string,
- how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
- test_socket->internal_socket_->fire_callback(how);
- }
- bool TestNrSocket::is_tcp_connection_behind_nat() const {
- return internal_socket_->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 RefPtr<NrSocketBase> &external_socket) const {
- r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
- internal_socket_->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 RefPtr<NrSocketBase> &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_DEBUG,
- "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_DEBUG, "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_DEBUG,
- "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_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
- send_queue_.push_back(RefPtr<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
|