LCOV - code coverage report
Current view: top level - media/mtransport - test_nr_socket.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 429 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 44 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : /*
       7             : */
       8             : 
       9             : /*
      10             : Based partially on original code from nICEr and nrappkit.
      11             : 
      12             : nICEr copyright:
      13             : 
      14             : Copyright (c) 2007, Adobe Systems, Incorporated
      15             : All rights reserved.
      16             : 
      17             : Redistribution and use in source and binary forms, with or without
      18             : modification, are permitted provided that the following conditions are
      19             : met:
      20             : 
      21             : * Redistributions of source code must retain the above copyright
      22             :   notice, this list of conditions and the following disclaimer.
      23             : 
      24             : * Redistributions in binary form must reproduce the above copyright
      25             :   notice, this list of conditions and the following disclaimer in the
      26             :   documentation and/or other materials provided with the distribution.
      27             : 
      28             : * Neither the name of Adobe Systems, Network Resonance nor the names of its
      29             :   contributors may be used to endorse or promote products derived from
      30             :   this software without specific prior written permission.
      31             : 
      32             : THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      33             : "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      34             : LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      35             : A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      36             : OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      37             : SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      38             : LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      39             : DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      40             : THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      41             : (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      42             : OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      43             : 
      44             : 
      45             : nrappkit copyright:
      46             : 
      47             :    Copyright (C) 2001-2003, Network Resonance, Inc.
      48             :    Copyright (C) 2006, Network Resonance, Inc.
      49             :    All Rights Reserved
      50             : 
      51             :    Redistribution and use in source and binary forms, with or without
      52             :    modification, are permitted provided that the following conditions
      53             :    are met:
      54             : 
      55             :    1. Redistributions of source code must retain the above copyright
      56             :       notice, this list of conditions and the following disclaimer.
      57             :    2. Redistributions in binary form must reproduce the above copyright
      58             :       notice, this list of conditions and the following disclaimer in the
      59             :       documentation and/or other materials provided with the distribution.
      60             :    3. Neither the name of Network Resonance, Inc. nor the name of any
      61             :       contributors to this software may be used to endorse or promote
      62             :       products derived from this software without specific prior written
      63             :       permission.
      64             : 
      65             :    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
      66             :    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      67             :    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      68             :    ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
      69             :    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      70             :    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      71             :    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      72             :    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      73             :    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      74             :    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      75             :    POSSIBILITY OF SUCH DAMAGE.
      76             : 
      77             : 
      78             :    ekr@rtfm.com  Thu Dec 20 20:14:49 2001
      79             : */
      80             : 
      81             : // Original author: bcampen@mozilla.com [:bwc]
      82             : 
      83             : extern "C" {
      84             : #include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
      85             : #include "nr_api.h"
      86             : #include "async_wait.h"
      87             : #include "async_timer.h"
      88             : #include "nr_socket.h"
      89             : #include "nr_socket_local.h"
      90             : #include "stun_hint.h"
      91             : #include "transport_addr.h"
      92             : }
      93             : 
      94             : #include "mozilla/RefPtr.h"
      95             : #include "test_nr_socket.h"
      96             : #include "runnable_utils.h"
      97             : 
      98             : namespace mozilla {
      99             : 
     100           0 : static int test_nat_socket_create(void *obj,
     101             :                                   nr_transport_addr *addr,
     102             :                                   nr_socket **sockp) {
     103           0 :   RefPtr<NrSocketBase> sock = new TestNrSocket(static_cast<TestNat*>(obj));
     104             : 
     105             :   int r, _status;
     106             : 
     107           0 :   r = sock->create(addr);
     108           0 :   if (r)
     109           0 :     ABORT(r);
     110             : 
     111           0 :   r = nr_socket_create_int(static_cast<void *>(sock),
     112           0 :                            sock->vtbl(), sockp);
     113           0 :   if (r)
     114           0 :     ABORT(r);
     115             : 
     116           0 :   _status = 0;
     117             : 
     118             :   {
     119             :     // We will release this reference in destroy(), not exactly the normal
     120             :     // ownership model, but it is what it is.
     121           0 :     NrSocketBase *dummy = sock.forget().take();
     122             :     (void)dummy;
     123             :   }
     124             : 
     125             : abort:
     126           0 :   return _status;
     127             : }
     128             : 
     129           0 : static int test_nat_socket_factory_destroy(void **obj) {
     130           0 :   TestNat *nat = static_cast<TestNat*>(*obj);
     131           0 :   *obj = nullptr;
     132           0 :   nat->Release();
     133           0 :   return 0;
     134             : }
     135             : 
     136             : static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = {
     137             :   test_nat_socket_create,
     138             :   test_nat_socket_factory_destroy
     139             : };
     140             : 
     141             : /* static */
     142             : TestNat::NatBehavior
     143           0 : TestNat::ToNatBehavior(const std::string& type) {
     144           0 :   if (!type.compare("ENDPOINT_INDEPENDENT")) {
     145           0 :     return TestNat::ENDPOINT_INDEPENDENT;
     146             :   }
     147           0 :   if (!type.compare("ADDRESS_DEPENDENT")) {
     148           0 :     return TestNat::ADDRESS_DEPENDENT;
     149             :   }
     150           0 :   if (!type.compare("PORT_DEPENDENT")) {
     151           0 :     return TestNat::PORT_DEPENDENT;
     152             :   }
     153             : 
     154           0 :   MOZ_ASSERT(false, "Invalid NAT behavior");
     155             :   return TestNat::ENDPOINT_INDEPENDENT;
     156             : }
     157             : 
     158           0 : bool TestNat::has_port_mappings() const {
     159           0 :   for (TestNrSocket *sock : sockets_) {
     160           0 :     if (sock->has_port_mappings()) {
     161           0 :       return true;
     162             :     }
     163             :   }
     164           0 :   return false;
     165             : }
     166             : 
     167           0 : bool TestNat::is_my_external_tuple(const nr_transport_addr &addr) const {
     168           0 :   for (TestNrSocket *sock : sockets_) {
     169           0 :     if (sock->is_my_external_tuple(addr)) {
     170           0 :       return true;
     171             :     }
     172             :   }
     173             : 
     174           0 :   return false;
     175             : }
     176             : 
     177           0 : bool TestNat::is_an_internal_tuple(const nr_transport_addr &addr) const {
     178           0 :   for (TestNrSocket *sock : sockets_) {
     179             :     nr_transport_addr addr_behind_nat;
     180           0 :     if (sock->getaddr(&addr_behind_nat)) {
     181           0 :       MOZ_CRASH("TestNrSocket::getaddr failed!");
     182             :     }
     183             : 
     184             :     // TODO(bug 1170299): Remove const_cast when no longer necessary
     185           0 :     if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
     186             :                                &addr_behind_nat,
     187             :                                NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
     188           0 :       return true;
     189             :     }
     190             :   }
     191           0 :   return false;
     192             : }
     193             : 
     194           0 : int TestNat::create_socket_factory(nr_socket_factory **factorypp) {
     195             :   int r = nr_socket_factory_create_int(this,
     196             :                                        &test_nat_socket_factory_vtbl,
     197           0 :                                        factorypp);
     198           0 :   if (!r) {
     199           0 :     AddRef();
     200             :   }
     201           0 :   return r;
     202             : }
     203             : 
     204           0 : TestNrSocket::TestNrSocket(TestNat *nat)
     205             :   : nat_(nat),
     206             :     tls_(false),
     207           0 :     timer_handle_(nullptr) {
     208           0 :   nat_->insert_socket(this);
     209           0 : }
     210             : 
     211           0 : TestNrSocket::~TestNrSocket() {
     212           0 :   nat_->erase_socket(this);
     213           0 : }
     214             : 
     215           0 : RefPtr<NrSocketBase> TestNrSocket::create_external_socket(
     216             :     const nr_transport_addr &dest_addr) const {
     217           0 :   MOZ_ASSERT(nat_->enabled_);
     218           0 :   MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
     219             : 
     220             :   int r;
     221             :   nr_transport_addr nat_external_addr;
     222             : 
     223             :   // Open the socket on an arbitrary port, on the same address.
     224             :   // TODO(bug 1170299): Remove const_cast when no longer necessary
     225           0 :   if ((r = nr_transport_addr_copy(
     226             :              &nat_external_addr,
     227           0 :              const_cast<nr_transport_addr*>(&internal_socket_->my_addr())))) {
     228             :     r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
     229           0 :                                 __FUNCTION__, r);
     230           0 :     return nullptr;
     231             :   }
     232             : 
     233           0 :   if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) {
     234             :     r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_set_port: %d",
     235           0 :                                 __FUNCTION__, r);
     236           0 :     return nullptr;
     237             :   }
     238             : 
     239           0 :   RefPtr<NrSocketBase> external_socket;
     240           0 :   r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket);
     241             : 
     242           0 :   if (r) {
     243             :     r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d",
     244           0 :                                 __FUNCTION__, r);
     245           0 :     return nullptr;
     246             :   }
     247             : 
     248           0 :   return external_socket;
     249             : }
     250             : 
     251           0 : int TestNrSocket::create(nr_transport_addr *addr) {
     252           0 :   if (addr->tls_host[0] != '\0') {
     253           0 :     tls_ = true;
     254             :   }
     255             : 
     256           0 :   return NrSocketBase::CreateSocket(addr, &internal_socket_);
     257             : }
     258             : 
     259           0 : int TestNrSocket::getaddr(nr_transport_addr *addrp) {
     260           0 :   return internal_socket_->getaddr(addrp);
     261             : }
     262             : 
     263           0 : void TestNrSocket::close() {
     264           0 :   if (timer_handle_) {
     265           0 :     NR_async_timer_cancel(timer_handle_);
     266           0 :     timer_handle_ = nullptr;
     267             :   }
     268           0 :   internal_socket_->close();
     269           0 :   for (RefPtr<PortMapping>& port_mapping : port_mappings_) {
     270           0 :     port_mapping->external_socket_->close();
     271             :   }
     272           0 : }
     273             : 
     274           0 : int TestNrSocket::listen(int backlog) {
     275           0 :   MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
     276           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     277             :         "TestNrSocket %s listening",
     278           0 :         internal_socket_->my_addr().as_string);
     279             : 
     280           0 :   return internal_socket_->listen(backlog);
     281             : }
     282             : 
     283           0 : int TestNrSocket::accept(nr_transport_addr *addrp, nr_socket **sockp) {
     284           0 :   MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
     285           0 :   int r = internal_socket_->accept(addrp, sockp);
     286           0 :   if (r) {
     287           0 :     return r;
     288             :   }
     289             : 
     290           0 :   if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) {
     291           0 :     nr_socket_destroy(sockp);
     292           0 :     return R_IO_ERROR;
     293             :   }
     294             : 
     295           0 :   return 0;
     296             : }
     297             : 
     298           0 : void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void *cb_arg) {
     299           0 :   DeferredPacket *op = static_cast<DeferredPacket *>(cb_arg);
     300           0 :   op->socket_->timer_handle_ = nullptr;
     301           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     302             :         "TestNrSocket %s sending delayed STUN response",
     303           0 :         op->internal_socket_->my_addr().as_string);
     304           0 :   op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(),
     305           0 :                                op->flags_, &op->to_);
     306             : 
     307           0 :   delete op;
     308           0 : }
     309             : 
     310           0 : int TestNrSocket::sendto(const void *msg, size_t len,
     311             :                          int flags, nr_transport_addr *to) {
     312           0 :   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
     313             : 
     314           0 :   if (nat_->nat_delegate_ && nat_->nat_delegate_->on_sendto(nat_, msg, len, flags, to)) {
     315           0 :     return 0;
     316             :   }
     317             : 
     318           0 :   UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
     319           0 :   if (nat_->block_stun_ &&
     320           0 :       nr_is_stun_message(buf, len)) {
     321           0 :     return 0;
     322             :   }
     323             : 
     324             :   /* TODO: improve the functionality of this in bug 1253657 */
     325           0 :   if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
     326           0 :     if (nat_->delay_stun_resp_ms_ &&
     327           0 :         nr_is_stun_response_message(buf, len)) {
     328           0 :       NR_ASYNC_TIMER_SET(nat_->delay_stun_resp_ms_,
     329             :                          process_delayed_cb,
     330             :                          new DeferredPacket(this, msg, len, flags, to,
     331             :                                             internal_socket_),
     332           0 :                          &timer_handle_);
     333           0 :       return 0;
     334             :     }
     335           0 :     return internal_socket_->sendto(msg, len, flags, to);
     336             :   }
     337             : 
     338           0 :   destroy_stale_port_mappings();
     339             : 
     340           0 :   if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
     341             :     // Silently eat the packet
     342           0 :     return 0;
     343             :   }
     344             : 
     345             :   // Choose our port mapping based on our most selective criteria
     346           0 :   PortMapping *port_mapping = get_port_mapping(*to,
     347           0 :                                                std::max(nat_->filtering_type_,
     348           0 :                                                         nat_->mapping_type_));
     349             : 
     350           0 :   if (!port_mapping) {
     351             :     // See if we have already made the external socket we need to use.
     352             :     PortMapping *similar_port_mapping =
     353           0 :       get_port_mapping(*to, nat_->mapping_type_);
     354           0 :     RefPtr<NrSocketBase> external_socket;
     355             : 
     356           0 :     if (similar_port_mapping) {
     357           0 :       external_socket = similar_port_mapping->external_socket_;
     358             :     } else {
     359           0 :       external_socket = create_external_socket(*to);
     360           0 :       if (!external_socket) {
     361           0 :         MOZ_ASSERT(false);
     362             :         return R_INTERNAL;
     363             :       }
     364             :     }
     365             : 
     366           0 :     port_mapping = create_port_mapping(*to, external_socket);
     367           0 :     port_mappings_.push_back(port_mapping);
     368             : 
     369           0 :     if (poll_flags() & PR_POLL_READ) {
     370             :       // Make sure the new port mapping is ready to receive traffic if the
     371             :       // TestNrSocket is already waiting.
     372             :       port_mapping->async_wait(NR_ASYNC_WAIT_READ,
     373             :                               socket_readable_callback,
     374             :                               this,
     375             :                               (char*)__FUNCTION__,
     376           0 :                               __LINE__);
     377             :     }
     378             :   }
     379             : 
     380             :   // We probably don't want to propagate the flags, since this is a simulated
     381             :   // external IP address.
     382           0 :   return port_mapping->sendto(msg, len, *to);
     383             : }
     384             : 
     385           0 : int TestNrSocket::recvfrom(void *buf, size_t maxlen,
     386             :                            size_t *len, int flags,
     387             :                            nr_transport_addr *from) {
     388           0 :   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
     389             : 
     390             :   int r;
     391           0 :   bool ingress_allowed = false;
     392             : 
     393           0 :   if (readable_socket_) {
     394             :     // If any of the external sockets got data, see if it will be passed through
     395           0 :     r = readable_socket_->recvfrom(buf, maxlen, len, 0, from);
     396           0 :     readable_socket_ = nullptr;
     397           0 :     if (!r) {
     398             :       PortMapping *port_mapping_used;
     399           0 :       ingress_allowed = allow_ingress(*from, &port_mapping_used);
     400           0 :       if (ingress_allowed) {
     401           0 :         r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s",
     402           0 :               internal_socket_->my_addr().as_string,
     403             :               from->as_string,
     404           0 :               port_mapping_used->external_socket_->my_addr().as_string);
     405           0 :         if (nat_->refresh_on_ingress_) {
     406           0 :           port_mapping_used->last_used_ = PR_IntervalNow();
     407             :         }
     408             :       }
     409             :     }
     410             :   } else {
     411             :     // If no external socket has data, see if there's any data that was sent
     412             :     // directly to the TestNrSocket, and eat it if it isn't supposed to get
     413             :     // through.
     414           0 :     r = internal_socket_->recvfrom(buf, maxlen, len, flags, from);
     415           0 :     if (!r) {
     416             :       // We do not use allow_ingress() here because that only handles traffic
     417             :       // landing on an external port.
     418           0 :       ingress_allowed = (!nat_->enabled_ ||
     419           0 :                          nat_->is_an_internal_tuple(*from));
     420           0 :       if (!ingress_allowed) {
     421           0 :         r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
     422             :               "Not behind the same NAT",
     423           0 :               internal_socket_->my_addr().as_string,
     424           0 :               from->as_string);
     425             :       } else {
     426           0 :         r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s",
     427           0 :               internal_socket_->my_addr().as_string,
     428           0 :               from->as_string);
     429             :       }
     430             :     }
     431             :   }
     432             : 
     433             :   // Kinda lame that we are forced to give the app a readable callback and then
     434             :   // say "Oh, never mind...", but the alternative is to totally decouple the
     435             :   // callbacks from STS and the callbacks the app sets. On the bright side, this
     436             :   // speeds up unit tests where we are verifying that ingress is forbidden,
     437             :   // since they'll get a readable callback and then an error, instead of having
     438             :   // to wait for a timeout.
     439           0 :   if (!ingress_allowed) {
     440           0 :     *len = 0;
     441           0 :     r = R_WOULDBLOCK;
     442             :   }
     443             : 
     444           0 :   return r;
     445             : }
     446             : 
     447           0 : bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
     448             :                                  PortMapping **port_mapping_used) const {
     449             :   // This is only called for traffic arriving at a port mapping
     450           0 :   MOZ_ASSERT(nat_->enabled_);
     451           0 :   MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
     452             : 
     453           0 :   *port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
     454           0 :   if (!(*port_mapping_used)) {
     455           0 :     r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
     456             :                                  "Filtered",
     457           0 :                                  internal_socket_->my_addr().as_string,
     458           0 :                                  from.as_string);
     459           0 :     return false;
     460             :   }
     461             : 
     462           0 :   if (is_port_mapping_stale(**port_mapping_used)) {
     463           0 :     r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
     464             :                                  "Stale port mapping",
     465           0 :                                  internal_socket_->my_addr().as_string,
     466           0 :                                  from.as_string);
     467           0 :     return false;
     468             :   }
     469             : 
     470           0 :   if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
     471           0 :     r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
     472             :                                  "Hairpinning disallowed",
     473           0 :                                  internal_socket_->my_addr().as_string,
     474           0 :                                  from.as_string);
     475           0 :     return false;
     476             :   }
     477             : 
     478           0 :   return true;
     479             : }
     480             : 
     481           0 : int TestNrSocket::connect(nr_transport_addr *addr) {
     482             : 
     483           0 :   if (connect_invoked_ || !port_mappings_.empty()) {
     484           0 :     MOZ_CRASH("TestNrSocket::connect() called more than once!");
     485             :     return R_INTERNAL;
     486             :   }
     487             : 
     488           0 :   if (!nat_->enabled_
     489           0 :       || addr->protocol==IPPROTO_UDP  // Horrible hack to allow default address
     490             :                                       // discovery to work. Only works because
     491             :                                       // we don't normally connect on UDP.
     492           0 :       || nat_->is_an_internal_tuple(*addr)) {
     493             :     // This will set connect_invoked_
     494           0 :     return internal_socket_->connect(addr);
     495             :   }
     496             : 
     497           0 :   RefPtr<NrSocketBase> external_socket(create_external_socket(*addr));
     498           0 :   if (!external_socket) {
     499           0 :     return R_INTERNAL;
     500             :   }
     501             : 
     502           0 :   PortMapping *port_mapping = create_port_mapping(*addr, external_socket);
     503           0 :   port_mappings_.push_back(port_mapping);
     504           0 :   int r = port_mapping->external_socket_->connect(addr);
     505           0 :   if (r && r != R_WOULDBLOCK) {
     506           0 :     return r;
     507             :   }
     508             : 
     509           0 :   port_mapping->last_used_ = PR_IntervalNow();
     510             : 
     511           0 :   if (poll_flags() & PR_POLL_READ) {
     512             :     port_mapping->async_wait(NR_ASYNC_WAIT_READ,
     513             :                              port_mapping_tcp_passthrough_callback,
     514             :                              this,
     515             :                              (char*)__FUNCTION__,
     516           0 :                              __LINE__);
     517             :   }
     518             : 
     519           0 :   return r;
     520             : }
     521             : 
     522           0 : int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
     523           0 :   UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
     524             : 
     525           0 :   if (nat_->nat_delegate_ && nat_->nat_delegate_->on_write(nat_, msg, len, written)) {
     526           0 :     return R_INTERNAL;
     527             :   }
     528             : 
     529           0 :   if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
     530             :     // Should cause this socket to be abandoned
     531           0 :     r_log(LOG_GENERIC, LOG_DEBUG,
     532             :           "TestNrSocket %s dropping outgoing TCP "
     533             :           "because it is configured to drop STUN",
     534           0 :           my_addr().as_string);
     535           0 :     return R_INTERNAL;
     536             :   }
     537             : 
     538           0 :   if (nat_->block_tcp_ && !tls_) {
     539             :     // Should cause this socket to be abandoned
     540           0 :     r_log(LOG_GENERIC, LOG_DEBUG,
     541             :           "TestNrSocket %s dropping outgoing TCP "
     542             :           "because it is configured to drop TCP",
     543           0 :           my_addr().as_string);
     544           0 :     return R_INTERNAL;
     545             :   }
     546             : 
     547           0 :   if (port_mappings_.empty()) {
     548             :     // The no-nat case, just pass call through.
     549           0 :     r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
     550           0 :           my_addr().as_string);
     551             : 
     552           0 :     return internal_socket_->write(msg, len, written);
     553             :   }
     554           0 :   destroy_stale_port_mappings();
     555           0 :   if (port_mappings_.empty()) {
     556           0 :     r_log(LOG_GENERIC, LOG_DEBUG,
     557             :           "TestNrSocket %s dropping outgoing TCP "
     558             :           "because the port mapping was stale",
     559           0 :           my_addr().as_string);
     560           0 :     return R_INTERNAL;
     561             :   }
     562             :   // This is TCP only
     563           0 :   MOZ_ASSERT(port_mappings_.size() == 1);
     564           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     565             :         "PortMapping %s -> %s writing",
     566           0 :         port_mappings_.front()->external_socket_->my_addr().as_string,
     567           0 :         port_mappings_.front()->remote_address_.as_string);
     568           0 :   port_mappings_.front()->last_used_ = PR_IntervalNow();
     569           0 :   return port_mappings_.front()->external_socket_->write(msg, len, written);
     570             : }
     571             : 
     572           0 : int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
     573             :   int r;
     574             : 
     575           0 :   if (port_mappings_.empty()) {
     576           0 :     r = internal_socket_->read(buf, maxlen, len);
     577             :   } else {
     578           0 :     MOZ_ASSERT(port_mappings_.size() == 1);
     579           0 :     r = port_mappings_.front()->external_socket_->read(buf, maxlen, len);
     580           0 :     if (!r && nat_->refresh_on_ingress_) {
     581           0 :       port_mappings_.front()->last_used_ = PR_IntervalNow();
     582             :     }
     583             :   }
     584             : 
     585           0 :   if (r) {
     586           0 :     return r;
     587             :   }
     588             : 
     589           0 :   if (nat_->nat_delegate_ && nat_->nat_delegate_->on_read(nat_, buf, maxlen, len)) {
     590           0 :     return R_INTERNAL;
     591             :   }
     592             : 
     593           0 :   if (nat_->block_tcp_ && !tls_) {
     594             :     // Should cause this socket to be abandoned
     595           0 :     return R_INTERNAL;
     596             :   }
     597             : 
     598           0 :   UCHAR *cbuf = static_cast<UCHAR*>(const_cast<void*>(buf));
     599           0 :   if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) {
     600             :     // Should cause this socket to be abandoned
     601           0 :     return R_INTERNAL;
     602             :   }
     603             : 
     604           0 :   return r;
     605             : }
     606             : 
     607           0 : int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
     608             :                              char *function, int line) {
     609           0 :   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
     610           0 :                                 internal_socket_->my_addr().as_string,
     611           0 :                                 how == NR_ASYNC_WAIT_READ ? "read" : "write");
     612             : 
     613             :   int r;
     614             : 
     615           0 :   if (how == NR_ASYNC_WAIT_READ) {
     616           0 :     NrSocketBase::async_wait(how, cb, cb_arg, function, line);
     617             : 
     618             :     // Make sure we're waiting on the socket for the internal address
     619           0 :     r = internal_socket_->async_wait(how,
     620             :                                      socket_readable_callback,
     621             :                                      this,
     622             :                                      function,
     623           0 :                                      line);
     624             :   } else {
     625             :     // For write, just use the readiness of the internal socket, since we queue
     626             :     // everything for the port mappings.
     627           0 :     r = internal_socket_->async_wait(how,
     628             :                                      cb,
     629             :                                      cb_arg,
     630             :                                      function,
     631           0 :                                      line);
     632             :   }
     633             : 
     634           0 :   if (r) {
     635           0 :     r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
     636             :                                 "internal socket: %d\n",
     637           0 :                                 internal_socket_->my_addr().as_string,
     638           0 :                                 r);
     639           0 :     return r;
     640             :   }
     641             : 
     642           0 :   if (is_tcp_connection_behind_nat()) {
     643             :     // Bypass all port-mapping related logic
     644           0 :     return 0;
     645             :   }
     646             : 
     647           0 :   if (internal_socket_->my_addr().protocol == IPPROTO_TCP) {
     648             :     // For a TCP connection through a simulated NAT, these signals are
     649             :     // just passed through.
     650           0 :     MOZ_ASSERT(port_mappings_.size() == 1);
     651             : 
     652           0 :     return port_mappings_.front()->async_wait(
     653             :         how,
     654             :         port_mapping_tcp_passthrough_callback,
     655             :         this,
     656             :         function,
     657           0 :         line);
     658             :   }
     659           0 :   if (how == NR_ASYNC_WAIT_READ) {
     660             :     // For UDP port mappings, we decouple the writeable callbacks
     661           0 :     for (PortMapping *port_mapping : port_mappings_) {
     662             :       // Be ready to receive traffic on our port mappings
     663             :       r = port_mapping->async_wait(how,
     664             :                                    socket_readable_callback,
     665             :                                    this,
     666             :                                    function,
     667           0 :                                    line);
     668           0 :       if (r) {
     669           0 :         r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
     670             :                                     "port mapping: %d\n",
     671           0 :                                     internal_socket_->my_addr().as_string,
     672           0 :                                     r);
     673           0 :         return r;
     674             :       }
     675             :     }
     676             :   }
     677             : 
     678           0 :   return 0;
     679             : }
     680             : 
     681           0 : void TestNrSocket::cancel_port_mapping_async_wait(int how) {
     682           0 :   for (PortMapping *port_mapping : port_mappings_) {
     683           0 :     port_mapping->cancel(how);
     684             :   }
     685           0 : }
     686             : 
     687           0 : int TestNrSocket::cancel(int how) {
     688             : 
     689           0 :   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
     690           0 :         internal_socket_->my_addr().as_string,
     691           0 :         how == NR_ASYNC_WAIT_READ ? "read" : "write");
     692             : 
     693             :   // Writable callbacks are decoupled except for the TCP case
     694           0 :   if (how == NR_ASYNC_WAIT_READ ||
     695           0 :       internal_socket_->my_addr().protocol == IPPROTO_TCP) {
     696           0 :     cancel_port_mapping_async_wait(how);
     697             :   }
     698             : 
     699           0 :   return internal_socket_->cancel(how);
     700             : }
     701             : 
     702           0 : bool TestNrSocket::has_port_mappings() const {
     703           0 :   return !port_mappings_.empty();
     704             : }
     705             : 
     706           0 : bool TestNrSocket::is_my_external_tuple(const nr_transport_addr &addr) const {
     707           0 :   for (PortMapping *port_mapping : port_mappings_) {
     708             :     nr_transport_addr port_mapping_addr;
     709           0 :     if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) {
     710           0 :       MOZ_CRASH("NrSocket::getaddr failed!");
     711             :     }
     712             : 
     713             :     // TODO(bug 1170299): Remove const_cast when no longer necessary
     714           0 :     if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
     715             :                                &port_mapping_addr,
     716             :                                NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
     717           0 :       return true;
     718             :     }
     719             :   }
     720           0 :   return false;
     721             : }
     722             : 
     723           0 : bool TestNrSocket::is_port_mapping_stale(
     724             :     const PortMapping &port_mapping) const {
     725           0 :   PRIntervalTime now = PR_IntervalNow();
     726           0 :   PRIntervalTime elapsed_ticks = now - port_mapping.last_used_;
     727           0 :   uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks);
     728           0 :   return idle_duration > nat_->mapping_timeout_;
     729             : }
     730             : 
     731           0 : void TestNrSocket::destroy_stale_port_mappings() {
     732           0 :   for (auto i = port_mappings_.begin(); i != port_mappings_.end();) {
     733           0 :     auto temp = i;
     734           0 :     ++i;
     735           0 :     if (is_port_mapping_stale(**temp)) {
     736           0 :       r_log(LOG_GENERIC, LOG_INFO,
     737             :             "TestNrSocket %s destroying port mapping %s -> %s",
     738           0 :             internal_socket_->my_addr().as_string,
     739           0 :             (*temp)->external_socket_->my_addr().as_string,
     740           0 :             (*temp)->remote_address_.as_string);
     741             : 
     742           0 :       port_mappings_.erase(temp);
     743             :     }
     744             :   }
     745           0 : }
     746             : 
     747           0 : void TestNrSocket::socket_readable_callback(void *real_sock_v,
     748             :                                              int how,
     749             :                                              void *test_sock_v) {
     750           0 :   TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
     751           0 :   NrSocketBase *real_socket = static_cast<NrSocketBase*>(real_sock_v);
     752             : 
     753           0 :   test_socket->on_socket_readable(real_socket);
     754           0 : }
     755             : 
     756           0 : void TestNrSocket::on_socket_readable(NrSocketBase *real_socket) {
     757           0 :   if (!readable_socket_ && (real_socket != internal_socket_)) {
     758           0 :     readable_socket_ = real_socket;
     759             :   }
     760             : 
     761           0 :   fire_readable_callback();
     762           0 : }
     763             : 
     764           0 : void TestNrSocket::fire_readable_callback() {
     765           0 :   MOZ_ASSERT(poll_flags() & PR_POLL_READ);
     766           0 :   r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
     767           0 :         internal_socket_->my_addr().as_string);
     768           0 :   fire_callback(NR_ASYNC_WAIT_READ);
     769           0 : }
     770             : 
     771           0 : void TestNrSocket::port_mapping_writeable_callback(void *ext_sock_v,
     772             :                                                    int how,
     773             :                                                    void *test_sock_v) {
     774           0 :   TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
     775           0 :   NrSocketBase *external_socket = static_cast<NrSocketBase*>(ext_sock_v);
     776             : 
     777           0 :   test_socket->write_to_port_mapping(external_socket);
     778           0 : }
     779             : 
     780           0 : void TestNrSocket::write_to_port_mapping(NrSocketBase *external_socket) {
     781           0 :   MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
     782             : 
     783           0 :   int r = 0;
     784           0 :   for (PortMapping *port_mapping : port_mappings_) {
     785           0 :     if (port_mapping->external_socket_ == external_socket) {
     786             :       // If the send succeeds, or if there was nothing to send, we keep going
     787           0 :       r = port_mapping->send_from_queue();
     788           0 :       if (r) {
     789           0 :         break;
     790             :       }
     791             :     }
     792             :   }
     793             : 
     794           0 :   if (r == R_WOULDBLOCK) {
     795             :     // Re-register for writeable callbacks, since we still have stuff to send
     796             :     NR_ASYNC_WAIT(external_socket,
     797             :                   NR_ASYNC_WAIT_WRITE,
     798             :                   &TestNrSocket::port_mapping_writeable_callback,
     799           0 :                   this);
     800             :   }
     801           0 : }
     802             : 
     803           0 : void TestNrSocket::port_mapping_tcp_passthrough_callback(void *ext_sock_v,
     804             :                                                          int how,
     805             :                                                          void *test_sock_v) {
     806           0 :   TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
     807           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     808             :         "TestNrSocket %s firing %s callback",
     809           0 :         test_socket->internal_socket_->my_addr().as_string,
     810           0 :         how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
     811             : 
     812             : 
     813           0 :   test_socket->internal_socket_->fire_callback(how);
     814           0 : }
     815             : 
     816           0 : bool TestNrSocket::is_tcp_connection_behind_nat() const {
     817           0 :   return internal_socket_->my_addr().protocol == IPPROTO_TCP &&
     818           0 :          port_mappings_.empty();
     819             : }
     820             : 
     821           0 : TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
     822             :     const nr_transport_addr &remote_address,
     823             :     TestNat::NatBehavior filter) const {
     824             :   int compare_flags;
     825           0 :   switch (filter) {
     826             :     case TestNat::ENDPOINT_INDEPENDENT:
     827           0 :       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL;
     828           0 :       break;
     829             :     case TestNat::ADDRESS_DEPENDENT:
     830           0 :       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR;
     831           0 :       break;
     832             :     case TestNat::PORT_DEPENDENT:
     833           0 :       compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL;
     834           0 :       break;
     835             :   }
     836             : 
     837           0 :   for (PortMapping *port_mapping : port_mappings_) {
     838             :     // TODO(bug 1170299): Remove const_cast when no longer necessary
     839           0 :     if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&remote_address),
     840             :                                &port_mapping->remote_address_,
     841             :                                compare_flags))
     842           0 :       return port_mapping;
     843             :   }
     844           0 :   return nullptr;
     845             : }
     846             : 
     847           0 : TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
     848             :     const nr_transport_addr &remote_address,
     849             :     const RefPtr<NrSocketBase> &external_socket) const {
     850           0 :   r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
     851           0 :         internal_socket_->my_addr().as_string,
     852           0 :         external_socket->my_addr().as_string,
     853           0 :         remote_address.as_string);
     854             : 
     855           0 :   return new PortMapping(remote_address, external_socket);
     856             : }
     857             : 
     858           0 : TestNrSocket::PortMapping::PortMapping(
     859             :     const nr_transport_addr &remote_address,
     860           0 :     const RefPtr<NrSocketBase> &external_socket) :
     861           0 :   external_socket_(external_socket) {
     862             :   // TODO(bug 1170299): Remove const_cast when no longer necessary
     863           0 :   nr_transport_addr_copy(&remote_address_,
     864           0 :                          const_cast<nr_transport_addr*>(&remote_address));
     865           0 : }
     866             : 
     867           0 : int TestNrSocket::PortMapping::send_from_queue() {
     868           0 :   MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
     869           0 :   int r = 0;
     870             : 
     871           0 :   while (!send_queue_.empty()) {
     872           0 :     UdpPacket &packet = *send_queue_.front();
     873           0 :     r_log(LOG_GENERIC, LOG_DEBUG,
     874             :           "PortMapping %s -> %s sending from queue to %s",
     875           0 :           external_socket_->my_addr().as_string,
     876             :           remote_address_.as_string,
     877           0 :           packet.remote_address_.as_string);
     878             : 
     879           0 :     r = external_socket_->sendto(packet.buffer_->data(),
     880             :                                  packet.buffer_->len(),
     881             :                                  0,
     882           0 :                                  &packet.remote_address_);
     883             : 
     884           0 :     if (r) {
     885           0 :       if (r != R_WOULDBLOCK) {
     886             :         r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying",
     887           0 :               __FUNCTION__, r);
     888           0 :         send_queue_.clear();
     889             :       } else {
     890           0 :         r_log(LOG_GENERIC, LOG_DEBUG, "Would block, will retry later");
     891             :       }
     892           0 :       break;
     893             :     }
     894             : 
     895           0 :     send_queue_.pop_front();
     896             :   }
     897             : 
     898           0 :   return r;
     899             : }
     900             : 
     901           0 : int TestNrSocket::PortMapping::sendto(const void *msg,
     902             :                                       size_t len,
     903             :                                       const nr_transport_addr &to) {
     904           0 :   MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
     905           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     906             :         "PortMapping %s -> %s sending to %s",
     907           0 :         external_socket_->my_addr().as_string,
     908             :         remote_address_.as_string,
     909           0 :         to.as_string);
     910             : 
     911           0 :   last_used_ = PR_IntervalNow();
     912           0 :   int r = external_socket_->sendto(msg, len, 0,
     913             :       // TODO(bug 1170299): Remove const_cast when no longer necessary
     914           0 :       const_cast<nr_transport_addr*>(&to));
     915             : 
     916           0 :   if (r == R_WOULDBLOCK) {
     917           0 :     r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
     918           0 :     send_queue_.push_back(RefPtr<UdpPacket>(new UdpPacket(msg, len, to)));
     919           0 :     return 0;
     920             :   }
     921           0 :   if (r) {
     922           0 :     r_log(LOG_GENERIC,LOG_ERR, "Error: %d", r);
     923             :   }
     924             : 
     925           0 :   return r;
     926             : }
     927             : 
     928           0 : int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void *cb_arg,
     929             :                                           char *function, int line) {
     930           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     931             :         "PortMapping %s -> %s waiting for %s",
     932           0 :         external_socket_->my_addr().as_string,
     933             :         remote_address_.as_string,
     934           0 :         how == NR_ASYNC_WAIT_READ ? "read" : "write");
     935             : 
     936           0 :   return external_socket_->async_wait(how, cb, cb_arg, function, line);
     937             : }
     938             : 
     939           0 : int TestNrSocket::PortMapping::cancel(int how) {
     940           0 :   r_log(LOG_GENERIC, LOG_DEBUG,
     941             :         "PortMapping %s -> %s stop waiting for %s",
     942           0 :         external_socket_->my_addr().as_string,
     943             :         remote_address_.as_string,
     944           0 :         how == NR_ASYNC_WAIT_READ ? "read" : "write");
     945             : 
     946           0 :   return external_socket_->cancel(how);
     947             : }
     948             : 
     949             : } // namespace mozilla
     950             : 

Generated by: LCOV version 1.13