tor-browser

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

test_nr_socket_ice_unittest.cpp (12713B)


      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 // Some of this code is taken from nricectx.cpp and nricemediastream.cpp
      8 // which in turn contains code cut-and-pasted from nICEr. Copyright is:
      9 
     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 "gtest/gtest.h"
     43 #include "gtest_utils.h"
     44 
     45 extern "C" {
     46 #include "ice_ctx.h"
     47 #include "ice_peer_ctx.h"
     48 #include "nICEr/src/net/transport_addr.h"
     49 }
     50 
     51 #include "mtransport_test_utils.h"
     52 #include "nricectx.h"
     53 #include "nricemediastream.h"
     54 #include "runnable_utils.h"
     55 #include "test_nr_socket.h"
     56 
     57 namespace mozilla {
     58 
     59 static unsigned int kDefaultTimeout = 7000;
     60 
     61 class IcePeer {
     62 public:
     63  IcePeer(const char* name, TestNat* nat, UINT4 flags,
     64          MtransportTestUtils* test_utils)
     65      : name_(name),
     66        ice_checking_(false),
     67        ice_connected_(false),
     68        ice_disconnected_(false),
     69        gather_cb_(false),
     70        stream_ready_(false),
     71        stream_failed_(false),
     72        ice_ctx_(nullptr),
     73        peer_ctx_(nullptr),
     74        nat_(nat),
     75        test_utils_(test_utils) {
     76    nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, nullptr,
     77                      &ice_ctx_);
     78 
     79    if (nat_) {
     80      nr_socket_factory* factory;
     81      nat_->create_socket_factory(&factory);
     82      nr_ice_ctx_set_socket_factory(ice_ctx_, factory);
     83    }
     84 
     85    // Create the handler objects
     86    ice_handler_vtbl_ = new nr_ice_handler_vtbl();
     87    ice_handler_vtbl_->select_pair = &IcePeer::select_pair;
     88    ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready;
     89    ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed;
     90    ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected;
     91    ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd;
     92    ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking;
     93    ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected;
     94 
     95    ice_handler_ = new nr_ice_handler();
     96    ice_handler_->vtbl = ice_handler_vtbl_;
     97    ice_handler_->obj = this;
     98 
     99    nr_ice_peer_ctx_create(ice_ctx_, ice_handler_,
    100                           const_cast<char*>(name_.c_str()), &peer_ctx_);
    101 
    102    nr_ice_add_media_stream(ice_ctx_, const_cast<char*>(name_.c_str()), "ufrag",
    103                            "pass", 2, &ice_media_stream_);
    104    EXPECT_EQ(2UL, GetStreamAttributes().size());
    105 
    106    nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_);
    107  }
    108 
    109  virtual ~IcePeer() { Destroy(); }
    110 
    111  void Destroy() {
    112    test_utils_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::Destroy_s));
    113  }
    114 
    115  void Destroy_s() {
    116    nr_ice_peer_ctx_destroy(&peer_ctx_);
    117    delete ice_handler_;
    118    delete ice_handler_vtbl_;
    119    nr_ice_ctx_destroy(&ice_ctx_);
    120  }
    121 
    122  void Gather(bool default_route_only = false) {
    123    test_utils_->SyncDispatchToSTS(
    124        WrapRunnable(this, &IcePeer::Gather_s, default_route_only));
    125  }
    126 
    127  void Gather_s(bool default_route_only = false) {
    128    int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this);
    129    ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK);
    130  }
    131 
    132  std::vector<std::string> GetStreamAttributes() {
    133    std::vector<std::string> attributes;
    134    test_utils_->SyncDispatchToSTS(
    135        WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s));
    136    return attributes;
    137  }
    138 
    139  std::vector<std::string> GetStreamAttributes_s() {
    140    char** attrs = nullptr;
    141    int attrct;
    142    std::vector<std::string> ret;
    143 
    144    int r =
    145        nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct);
    146    EXPECT_EQ(0, r);
    147 
    148    for (int i = 0; i < attrct; i++) {
    149      ret.push_back(std::string(attrs[i]));
    150      RFREE(attrs[i]);
    151    }
    152    RFREE(attrs);
    153 
    154    return ret;
    155  }
    156 
    157  std::vector<std::string> GetGlobalAttributes() {
    158    std::vector<std::string> attributes;
    159    test_utils_->SyncDispatchToSTS(
    160        WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s));
    161    return attributes;
    162  }
    163 
    164  std::vector<std::string> GetGlobalAttributes_s() {
    165    char** attrs = nullptr;
    166    int attrct;
    167    std::vector<std::string> ret;
    168 
    169    nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct);
    170 
    171    for (int i = 0; i < attrct; i++) {
    172      ret.push_back(std::string(attrs[i]));
    173      RFREE(attrs[i]);
    174    }
    175    RFREE(attrs);
    176 
    177    return ret;
    178  }
    179 
    180  void ParseGlobalAttributes(std::vector<std::string> attrs) {
    181    std::vector<char*> attrs_in;
    182    attrs_in.reserve(attrs.size());
    183    for (auto& attr : attrs) {
    184      attrs_in.push_back(const_cast<char*>(attr.c_str()));
    185    }
    186 
    187    int r = nr_ice_peer_ctx_parse_global_attributes(
    188        peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
    189    ASSERT_EQ(0, r);
    190  }
    191 
    192  void SetControlling(bool controlling) {
    193    peer_ctx_->controlling = controlling ? 1 : 0;
    194  }
    195 
    196  void SetRemoteAttributes(std::vector<std::string> attributes) {
    197    test_utils_->SyncDispatchToSTS(
    198        WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes));
    199  }
    200 
    201  void SetRemoteAttributes_s(std::vector<std::string> attributes) {
    202    int r;
    203 
    204    std::vector<char*> attrs;
    205    attrs.reserve(attributes.size());
    206    for (auto& attr : attributes) {
    207      attrs.push_back(const_cast<char*>(attr.c_str()));
    208    }
    209 
    210    if (!attrs.empty()) {
    211      r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_,
    212                                                  &attrs[0], attrs.size());
    213      ASSERT_EQ(0, r);
    214    }
    215  }
    216 
    217  void StartChecks() {
    218    test_utils_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::StartChecks_s));
    219  }
    220 
    221  void StartChecks_s() {
    222    int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_);
    223    ASSERT_EQ(0, r);
    224 
    225    r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1);
    226    ASSERT_EQ(0, r);
    227  }
    228 
    229  // Handler callbacks
    230  static int select_pair(void* obj, nr_ice_media_stream* stream,
    231                         int component_id, nr_ice_cand_pair** potentials,
    232                         int potential_ct) {
    233    return 0;
    234  }
    235 
    236  static int stream_ready(void* obj, nr_ice_media_stream* stream) {
    237    IcePeer* peer = static_cast<IcePeer*>(obj);
    238    peer->stream_ready_ = true;
    239    return 0;
    240  }
    241 
    242  static int stream_failed(void* obj, nr_ice_media_stream* stream) {
    243    IcePeer* peer = static_cast<IcePeer*>(obj);
    244    peer->stream_failed_ = true;
    245    return 0;
    246  }
    247 
    248  static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) {
    249    IcePeer* peer = static_cast<IcePeer*>(obj);
    250    peer->ice_checking_ = true;
    251    return 0;
    252  }
    253 
    254  static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) {
    255    IcePeer* peer = static_cast<IcePeer*>(obj);
    256    peer->ice_connected_ = true;
    257    return 0;
    258  }
    259 
    260  static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) {
    261    IcePeer* peer = static_cast<IcePeer*>(obj);
    262    peer->ice_disconnected_ = true;
    263    return 0;
    264  }
    265 
    266  static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx,
    267                       nr_ice_media_stream* stream, int component_id,
    268                       UCHAR* msg, int len) {
    269    return 0;
    270  }
    271 
    272  static void gather_cb(NR_SOCKET s, int h, void* arg) {
    273    IcePeer* peer = static_cast<IcePeer*>(arg);
    274    peer->gather_cb_ = true;
    275  }
    276 
    277  std::string name_;
    278 
    279  bool ice_checking_;
    280  bool ice_connected_;
    281  bool ice_disconnected_;
    282  bool gather_cb_;
    283  bool stream_ready_;
    284  bool stream_failed_;
    285 
    286  nr_ice_ctx* ice_ctx_;
    287  nr_ice_handler* ice_handler_;
    288  nr_ice_handler_vtbl* ice_handler_vtbl_;
    289  nr_ice_media_stream* ice_media_stream_;
    290  nr_ice_peer_ctx* peer_ctx_;
    291  TestNat* nat_;
    292  MtransportTestUtils* test_utils_;
    293 };
    294 
    295 class TestNrSocketIceUnitTest : public ::testing::Test {
    296 public:
    297  void SetUp() override {
    298    NSS_NoDB_Init(nullptr);
    299    NSS_SetDomesticPolicy();
    300 
    301    test_utils_ = new MtransportTestUtils();
    302    test_utils2_ = new MtransportTestUtils();
    303 
    304    test_utils_->SyncDispatchToSTS(
    305        WrapRunnable(this, &TestNrSocketIceUnitTest::SetUp_s));
    306  }
    307 
    308  void SetUp_s() { NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); }
    309 
    310  void TearDown() override {
    311    delete test_utils_;
    312    delete test_utils2_;
    313  }
    314 
    315  MtransportTestUtils* test_utils_;
    316  MtransportTestUtils* test_utils2_;
    317 };
    318 
    319 TEST_F(TestNrSocketIceUnitTest, TestIcePeer) {
    320  IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
    321               test_utils_);
    322  ASSERT_NE(peer.ice_ctx_, nullptr);
    323  ASSERT_NE(peer.peer_ctx_, nullptr);
    324  ASSERT_NE(peer.ice_media_stream_, nullptr);
    325  ASSERT_EQ(2UL, peer.GetStreamAttributes().size())
    326      << "Should have ice-ufrag and ice-pwd";
    327  peer.Gather();
    328  ASSERT_LT(2UL, peer.GetStreamAttributes().size())
    329      << "Should have ice-ufrag, ice-pwd, and at least one candidate.";
    330 }
    331 
    332 TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) {
    333  IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
    334               test_utils_);
    335  IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
    336                test_utils2_);
    337  peer.SetControlling(true);
    338  peer2.SetControlling(false);
    339 
    340  peer.Gather();
    341  peer2.Gather();
    342  std::vector<std::string> attrs = peer.GetGlobalAttributes();
    343  peer2.ParseGlobalAttributes(attrs);
    344  std::vector<std::string> attributes = peer.GetStreamAttributes();
    345  peer2.SetRemoteAttributes(attributes);
    346 
    347  attrs = peer2.GetGlobalAttributes();
    348  peer.ParseGlobalAttributes(attrs);
    349  attributes = peer2.GetStreamAttributes();
    350  peer.SetRemoteAttributes(attributes);
    351  peer2.StartChecks();
    352  peer.StartChecks();
    353 
    354  ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
    355  ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
    356 }
    357 
    358 TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) {
    359  IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
    360               test_utils_);
    361 
    362  RefPtr<TestNat> nat(new TestNat);
    363  class NatDelegate : public TestNat::NatDelegate {
    364   public:
    365    NatDelegate() : messages(0) {}
    366 
    367    int on_read(TestNat* nat, void* buf, size_t maxlen, size_t* len) override {
    368      return 0;
    369    }
    370 
    371    int on_sendto(TestNat* nat, const void* msg, size_t len, int flags,
    372                  const nr_transport_addr* to) override {
    373      ++messages;
    374      // 25% packet loss
    375      if (messages % 4 == 0) {
    376        return 1;
    377      }
    378      return 0;
    379    }
    380 
    381    int on_write(TestNat* nat, const void* msg, size_t len,
    382                 size_t* written) override {
    383      return 0;
    384    }
    385 
    386    int messages;
    387  } delegate;
    388  nat->nat_delegate_ = &delegate;
    389 
    390  IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
    391                test_utils2_);
    392  peer.SetControlling(true);
    393  peer2.SetControlling(false);
    394 
    395  peer.Gather();
    396  peer2.Gather();
    397  std::vector<std::string> attrs = peer.GetGlobalAttributes();
    398  peer2.ParseGlobalAttributes(attrs);
    399  std::vector<std::string> attributes = peer.GetStreamAttributes();
    400  peer2.SetRemoteAttributes(attributes);
    401 
    402  attrs = peer2.GetGlobalAttributes();
    403  peer.ParseGlobalAttributes(attrs);
    404  attributes = peer2.GetStreamAttributes();
    405  peer.SetRemoteAttributes(attributes);
    406  peer2.StartChecks();
    407  peer.StartChecks();
    408 
    409  ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
    410  ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
    411 }
    412 
    413 }  // namespace mozilla