tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

turn_unittest.cpp (12357B)


      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 // Original author: ekr@rtfm.com
      8 
      9 // Some code copied from nICEr. License is:
     10 /*
     11 Copyright (c) 2007, Adobe Systems, Incorporated
     12 All rights reserved.
     13 
     14 Redistribution and use in source and binary forms, with or without
     15 modification, are permitted provided that the following conditions are
     16 met:
     17 
     18 * Redistributions of source code must retain the above copyright
     19  notice, this list of conditions and the following disclaimer.
     20 
     21 * Redistributions in binary form must reproduce the above copyright
     22  notice, this list of conditions and the following disclaimer in the
     23  documentation and/or other materials provided with the distribution.
     24 
     25 * Neither the name of Adobe Systems, Network Resonance nor the names of its
     26  contributors may be used to endorse or promote products derived from
     27  this software without specific prior written permission.
     28 
     29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     40 */
     41 
     42 #include <stdlib.h>
     43 
     44 #include <iostream>
     45 
     46 #include "runnable_utils.h"
     47 
     48 #define GTEST_HAS_RTTI 0
     49 #include "gtest/gtest.h"
     50 #include "gtest_utils.h"
     51 
     52 #define USE_TURN
     53 
     54 // nICEr includes
     55 extern "C" {
     56 // clang-format off
     57 #include "nr_api.h"
     58 #include "transport_addr.h"
     59 #include "nr_crypto.h"
     60 #include "nr_socket.h"
     61 #include "nr_socket_local.h"
     62 #include "nr_socket_buffered_stun.h"
     63 #include "stun_client_ctx.h"
     64 #include "turn_client_ctx.h"
     65 // clang-format on
     66 }
     67 
     68 #include "nricectx.h"
     69 
     70 using namespace mozilla;
     71 
     72 MOZ_RUNINIT static std::string kDummyTurnServer("192.0.2.1");  // From RFC 5737
     73 
     74 class TurnClient : public MtransportTest {
     75 public:
     76  TurnClient()
     77      : real_socket_(nullptr),
     78        net_socket_(nullptr),
     79        buffered_socket_(nullptr),
     80        net_fd_(nullptr),
     81        turn_ctx_(nullptr),
     82        allocated_(false),
     83        received_(0),
     84        protocol_(IPPROTO_UDP) {}
     85 
     86  ~TurnClient() = default;
     87 
     88  static void SetUpTestCase() {
     89    NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
     90  }
     91 
     92  void SetTcp() { protocol_ = IPPROTO_TCP; }
     93 
     94  void Init_s() {
     95    int r;
     96    nr_transport_addr addr;
     97    r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
     98    ASSERT_EQ(0, r);
     99 
    100    r = nr_socket_local_create(nullptr, &addr, &real_socket_);
    101    ASSERT_EQ(0, r);
    102 
    103    if (protocol_ == IPPROTO_TCP) {
    104      int r = nr_socket_buffered_stun_create(
    105          real_socket_, 100000, TURN_TCP_FRAMING, &buffered_socket_);
    106      ASSERT_EQ(0, r);
    107      net_socket_ = buffered_socket_;
    108    } else {
    109      net_socket_ = real_socket_;
    110    }
    111 
    112    r = nr_str_port_to_transport_addr(turn_server_.c_str(), 3478, protocol_,
    113                                      &addr);
    114    ASSERT_EQ(0, r);
    115 
    116    std::vector<unsigned char> password_vec(turn_password_.begin(),
    117                                            turn_password_.end());
    118    Data password;
    119    INIT_DATA(password, &password_vec[0], password_vec.size());
    120    r = nr_turn_client_ctx_create("test", net_socket_, turn_user_.c_str(),
    121                                  &password, &addr, nullptr, &turn_ctx_);
    122    ASSERT_EQ(0, r);
    123 
    124    r = nr_socket_getfd(net_socket_, &net_fd_);
    125    ASSERT_EQ(0, r);
    126 
    127    NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb, (void*)this);
    128  }
    129 
    130  void TearDown_s() {
    131    nr_turn_client_ctx_destroy(&turn_ctx_);
    132    if (net_fd_) {
    133      NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
    134    }
    135 
    136    nr_socket_destroy(&buffered_socket_);
    137  }
    138 
    139  void TearDown() {
    140    test_utils_->SyncDispatchToSTS(WrapRunnable(this, &TurnClient::TearDown_s));
    141    MtransportTest::TearDown();
    142  }
    143 
    144  void Allocate_s() {
    145    Init_s();
    146    ASSERT_TRUE(turn_ctx_);
    147 
    148    int r = nr_turn_client_allocate(turn_ctx_, allocate_success_cb, this);
    149    ASSERT_EQ(0, r);
    150  }
    151 
    152  void Allocate(bool expect_success = true) {
    153    test_utils_->SyncDispatchToSTS(WrapRunnable(this, &TurnClient::Allocate_s));
    154 
    155    if (expect_success) {
    156      ASSERT_TRUE_WAIT(allocated_, 5000);
    157    } else {
    158      PR_Sleep(10000);
    159      ASSERT_FALSE(allocated_);
    160    }
    161  }
    162 
    163  void Allocated() {
    164    if (turn_ctx_->state != NR_TURN_CLIENT_STATE_ALLOCATED) {
    165      std::cerr << "Allocation failed" << std::endl;
    166      return;
    167    }
    168    allocated_ = true;
    169 
    170    int r;
    171    nr_transport_addr addr;
    172 
    173    r = nr_turn_client_get_relayed_address(turn_ctx_, &addr);
    174    ASSERT_EQ(0, r);
    175 
    176    relay_addr_ = addr.as_string;
    177 
    178    std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl;
    179  }
    180 
    181  void Deallocate_s() {
    182    ASSERT_TRUE(turn_ctx_);
    183 
    184    std::cerr << "De-Allocating..." << std::endl;
    185    int r = nr_turn_client_deallocate(turn_ctx_);
    186    ASSERT_EQ(0, r);
    187  }
    188 
    189  void Deallocate() {
    190    test_utils_->SyncDispatchToSTS(
    191        WrapRunnable(this, &TurnClient::Deallocate_s));
    192  }
    193 
    194  void RequestPermission_s(const std::string& target) {
    195    nr_transport_addr addr;
    196    int r;
    197 
    198    // Expected pattern here is "IP4:127.0.0.1:3487"
    199    ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
    200 
    201    size_t offset = target.rfind(':');
    202    ASSERT_NE(std::string::npos, offset);
    203 
    204    std::string host = target.substr(4, offset - 4);
    205    std::string port = target.substr(offset + 1);
    206 
    207    r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()),
    208                                      IPPROTO_UDP, &addr);
    209    ASSERT_EQ(0, r);
    210 
    211    r = nr_turn_client_ensure_perm(turn_ctx_, &addr);
    212    ASSERT_EQ(0, r);
    213  }
    214 
    215  void RequestPermission(const std::string& target) {
    216    test_utils_->SyncDispatchToSTS(
    217        WrapRunnable(this, &TurnClient::RequestPermission_s, target));
    218  }
    219 
    220  void Readable(NR_SOCKET s, int how, void* arg) {
    221    // Re-arm
    222    std::cerr << "Socket is readable" << std::endl;
    223    NR_ASYNC_WAIT(s, how, socket_readable_cb, arg);
    224 
    225    UCHAR buf[8192];
    226    size_t len_s;
    227    nr_transport_addr addr;
    228 
    229    int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr);
    230    if (r) {
    231      std::cerr << "Error reading from socket" << std::endl;
    232      return;
    233    }
    234 
    235    ASSERT_LT(len_s, (size_t)INT_MAX);
    236    int len = (int)len_s;
    237 
    238    if (nr_is_stun_response_message(buf, len)) {
    239      std::cerr << "STUN response" << std::endl;
    240      r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr);
    241 
    242      if (r && r != R_REJECTED && r != R_RETRY) {
    243        std::cerr << "Error processing STUN: " << r << std::endl;
    244      }
    245    } else if (nr_is_stun_indication_message(buf, len)) {
    246      std::cerr << "STUN indication" << std::endl;
    247 
    248      /* Process the indication */
    249      unsigned char data[NR_STUN_MAX_MESSAGE_SIZE];
    250      size_t datal;
    251      nr_transport_addr remote_addr;
    252 
    253      r = nr_turn_client_parse_data_indication(
    254          turn_ctx_, &addr, buf, len, data, &datal, sizeof(data), &remote_addr);
    255      ASSERT_EQ(0, r);
    256      std::cerr << "Received " << datal << " bytes from "
    257                << remote_addr.as_string << std::endl;
    258 
    259      received_ += datal;
    260 
    261      for (size_t i = 0; i < datal; i++) {
    262        ASSERT_EQ(i & 0xff, data[i]);
    263      }
    264    } else {
    265      if (nr_is_stun_message(buf, len)) {
    266        std::cerr << "STUN message of unexpected type" << std::endl;
    267      } else {
    268        std::cerr << "Not a STUN message" << std::endl;
    269      }
    270      return;
    271    }
    272  }
    273 
    274  void SendTo_s(const std::string& target, int expect_return) {
    275    nr_transport_addr addr;
    276    int r;
    277 
    278    // Expected pattern here is "IP4:127.0.0.1:3487"
    279    ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
    280 
    281    size_t offset = target.rfind(':');
    282    ASSERT_NE(std::string::npos, offset);
    283 
    284    std::string host = target.substr(4, offset - 4);
    285    std::string port = target.substr(offset + 1);
    286 
    287    r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()),
    288                                      IPPROTO_UDP, &addr);
    289    ASSERT_EQ(0, r);
    290 
    291    unsigned char test[100];
    292    for (size_t i = 0; i < sizeof(test); i++) {
    293      test[i] = i & 0xff;
    294    }
    295 
    296    std::cerr << "Sending test message to " << target << " ..." << std::endl;
    297 
    298    r = nr_turn_client_send_indication(turn_ctx_, test, sizeof(test), 0, &addr);
    299    if (expect_return >= 0) {
    300      ASSERT_EQ(expect_return, r);
    301    }
    302  }
    303 
    304  void SendTo(const std::string& target, int expect_return = 0) {
    305    test_utils_->SyncDispatchToSTS(
    306        WrapRunnable(this, &TurnClient::SendTo_s, target, expect_return));
    307  }
    308 
    309  int received() const { return received_; }
    310 
    311  static void socket_readable_cb(NR_SOCKET s, int how, void* arg) {
    312    static_cast<TurnClient*>(arg)->Readable(s, how, arg);
    313  }
    314 
    315  static void allocate_success_cb(NR_SOCKET s, int how, void* arg) {
    316    static_cast<TurnClient*>(arg)->Allocated();
    317  }
    318 
    319 protected:
    320  std::string turn_server_;
    321  nr_socket* real_socket_;
    322  nr_socket* net_socket_;
    323  nr_socket* buffered_socket_;
    324  NR_SOCKET net_fd_;
    325  nr_turn_client_ctx* turn_ctx_;
    326  std::string relay_addr_;
    327  bool allocated_;
    328  int received_;
    329  int protocol_;
    330 };
    331 
    332 TEST_F(TurnClient, Allocate) {
    333  if (WarnIfTurnNotConfigured()) return;
    334 
    335  Allocate();
    336 }
    337 
    338 TEST_F(TurnClient, AllocateTcp) {
    339  if (WarnIfTurnNotConfigured()) return;
    340 
    341  SetTcp();
    342  Allocate();
    343 }
    344 
    345 TEST_F(TurnClient, AllocateAndHold) {
    346  if (WarnIfTurnNotConfigured()) return;
    347 
    348  Allocate();
    349  PR_Sleep(20000);
    350  ASSERT_TRUE(turn_ctx_->state == NR_TURN_CLIENT_STATE_ALLOCATED);
    351 }
    352 
    353 TEST_F(TurnClient, SendToSelf) {
    354  if (WarnIfTurnNotConfigured()) return;
    355 
    356  Allocate();
    357  SendTo(relay_addr_);
    358  ASSERT_TRUE_WAIT(received() == 100, 5000);
    359  SendTo(relay_addr_);
    360  ASSERT_TRUE_WAIT(received() == 200, 1000);
    361 }
    362 
    363 TEST_F(TurnClient, SendToSelfTcp) {
    364  if (WarnIfTurnNotConfigured()) return;
    365 
    366  SetTcp();
    367  Allocate();
    368  SendTo(relay_addr_);
    369  ASSERT_TRUE_WAIT(received() == 100, 5000);
    370  SendTo(relay_addr_);
    371  ASSERT_TRUE_WAIT(received() == 200, 1000);
    372 }
    373 
    374 TEST_F(TurnClient, PermissionDenied) {
    375  if (WarnIfTurnNotConfigured()) return;
    376 
    377  Allocate();
    378  RequestPermission(relay_addr_);
    379  PR_Sleep(1000);
    380 
    381  /* Fake a 403 response */
    382  nr_turn_permission* perm;
    383  perm = STAILQ_FIRST(&turn_ctx_->permissions);
    384  ASSERT_TRUE(perm);
    385  while (perm) {
    386    perm->stun->last_error_code = 403;
    387    std::cerr << "Set 403's on permission" << std::endl;
    388    perm = STAILQ_NEXT(perm, entry);
    389  }
    390 
    391  SendTo(relay_addr_, R_NOT_PERMITTED);
    392  ASSERT_TRUE(received() == 0);
    393 
    394  // TODO: We should check if we can still send to a second destination, but
    395  //      we would need a second TURN client as one client can only handle one
    396  //      allocation (maybe as part of bug 1128128 ?).
    397 }
    398 
    399 TEST_F(TurnClient, DeallocateReceiveFailure) {
    400  if (WarnIfTurnNotConfigured()) return;
    401 
    402  Allocate();
    403  SendTo(relay_addr_);
    404  ASSERT_TRUE_WAIT(received() == 100, 5000);
    405  Deallocate();
    406  turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED;
    407  SendTo(relay_addr_);
    408  PR_Sleep(1000);
    409  ASSERT_TRUE(received() == 100);
    410 }
    411 
    412 TEST_F(TurnClient, DeallocateReceiveFailureTcp) {
    413  if (WarnIfTurnNotConfigured()) return;
    414 
    415  SetTcp();
    416  Allocate();
    417  SendTo(relay_addr_);
    418  ASSERT_TRUE_WAIT(received() == 100, 5000);
    419  Deallocate();
    420  turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED;
    421  /* Either the connection got closed by the TURN server already, then the send
    422   * is going to fail, which we simply ignore. Or the connection is still alive
    423   * and we cand send the data, but it should not get forwarded to us. In either
    424   * case we should not receive more data. */
    425  SendTo(relay_addr_, -1);
    426  PR_Sleep(1000);
    427  ASSERT_TRUE(received() == 100);
    428 }
    429 
    430 TEST_F(TurnClient, AllocateDummyServer) {
    431  if (WarnIfTurnNotConfigured()) return;
    432 
    433  turn_server_ = kDummyTurnServer;
    434  Allocate(false);
    435 }