tor-browser

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

multi_tcp_socket_unittest.cpp (16488B)


      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 #include <iostream>
      8 #include <vector>
      9 
     10 #include "mozilla/Atomics.h"
     11 #include "pk11pub.h"
     12 #include "runnable_utils.h"
     13 
     14 extern "C" {
     15 // clang-format off
     16 #include "nr_api.h"
     17 #include "nr_socket.h"
     18 #include "transport_addr.h"
     19 #include "nr_socket_multi_tcp.h"
     20 // clang-format on
     21 }
     22 
     23 #include "nricectx.h"
     24 #include "stunserver.h"
     25 
     26 #define GTEST_HAS_RTTI 0
     27 #include "gtest/gtest.h"
     28 #include "gtest_utils.h"
     29 
     30 using namespace mozilla;
     31 
     32 namespace {
     33 
     34 class MultiTcpSocketTest : public MtransportTest {
     35 public:
     36  MultiTcpSocketTest() : socks(3, nullptr), readable(false) {}
     37 
     38  void SetUp() {
     39    MtransportTest::SetUp();
     40 
     41    test_utils_->SyncDispatchToSTS(
     42        WrapRunnable(this, &MultiTcpSocketTest::SetUp_s));
     43  }
     44 
     45  void SetUp_s() {
     46    NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
     47    ice_ctx_ = NrIceCtx::Create("stun");
     48    TestStunTcpServer::GetInstance(AF_INET);
     49    TestStunTcpServer::GetInstance(AF_INET6);
     50  }
     51 
     52  void TearDown() {
     53    test_utils_->SyncDispatchToSTS(
     54        WrapRunnable(this, &MultiTcpSocketTest::Shutdown_s));
     55 
     56    MtransportTest::TearDown();
     57  }
     58 
     59  DISALLOW_COPY_ASSIGN(MultiTcpSocketTest);
     60 
     61  static void SockReadable(NR_SOCKET s, int how, void* arg) {
     62    MultiTcpSocketTest* obj = static_cast<MultiTcpSocketTest*>(arg);
     63    obj->SetReadable(true);
     64  }
     65 
     66  void Shutdown_s() {
     67    ice_ctx_ = nullptr;
     68    for (auto& sock : socks) {
     69      nr_socket_destroy(&sock);
     70    }
     71  }
     72 
     73  static uint16_t GetRandomPort() {
     74    uint16_t result;
     75    if (PK11_GenerateRandom((unsigned char*)&result, 2) != SECSuccess) {
     76      MOZ_ASSERT(false);
     77      return 0;
     78    }
     79    return result;
     80  }
     81 
     82  static uint16_t EnsureEphemeral(uint16_t port) {
     83    // IANA ephemeral port range (49152 to 65535)
     84    return port | 49152;
     85  }
     86 
     87  void Create_s(nr_socket_tcp_type tcp_type, std::string stun_server_addr,
     88                uint16_t stun_server_port, nr_socket** sock) {
     89    nr_transport_addr local;
     90    // Get start of port range for test
     91    static unsigned short port_s = GetRandomPort();
     92    int r;
     93 
     94    if (!stun_server_addr.empty()) {
     95      std::vector<NrIceStunServer> stun_servers;
     96      UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
     97          stun_server_addr, stun_server_port, kNrIceTransportTcp));
     98      stun_servers.push_back(*server);
     99 
    100      ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
    101    }
    102 
    103    r = 1;
    104    for (int tries = 10; tries && r; --tries) {
    105      r = nr_str_port_to_transport_addr(
    106          (char*)"127.0.0.1", EnsureEphemeral(port_s++), IPPROTO_TCP, &local);
    107      ASSERT_EQ(0, r);
    108 
    109      r = nr_socket_multi_tcp_create(ice_ctx_->ctx(), nullptr, &local, tcp_type,
    110                                     1, 2048, sock);
    111    }
    112 
    113    ASSERT_EQ(0, r);
    114    printf("Creating socket on %s\n", local.as_string);
    115    r = nr_socket_multi_tcp_set_readable_cb(
    116        *sock, &MultiTcpSocketTest::SockReadable, this);
    117    ASSERT_EQ(0, r);
    118  }
    119 
    120  nr_socket* Create(nr_socket_tcp_type tcp_type,
    121                    std::string stun_server_addr = "",
    122                    uint16_t stun_server_port = 0) {
    123    nr_socket* sock = nullptr;
    124    test_utils_->SyncDispatchToSTS(
    125        WrapRunnable(this, &MultiTcpSocketTest::Create_s, tcp_type,
    126                     stun_server_addr, stun_server_port, &sock));
    127    return sock;
    128  }
    129 
    130  void Listen_s(nr_socket* sock) {
    131    nr_transport_addr addr;
    132    int r = nr_socket_getaddr(sock, &addr);
    133    ASSERT_EQ(0, r);
    134    printf("Listening on %s\n", addr.as_string);
    135    r = nr_socket_listen(sock, 5);
    136    ASSERT_EQ(0, r);
    137  }
    138 
    139  void Listen(nr_socket* sock) {
    140    test_utils_->SyncDispatchToSTS(
    141        WrapRunnable(this, &MultiTcpSocketTest::Listen_s, sock));
    142  }
    143 
    144  void Destroy_s(nr_socket* sock) {
    145    int r = nr_socket_destroy(&sock);
    146    ASSERT_EQ(0, r);
    147  }
    148 
    149  void Destroy(nr_socket* sock) {
    150    test_utils_->SyncDispatchToSTS(
    151        WrapRunnable(this, &MultiTcpSocketTest::Destroy_s, sock));
    152  }
    153 
    154  void Connect_s(nr_socket* from, nr_socket* to) {
    155    nr_transport_addr addr_to;
    156    nr_transport_addr addr_from;
    157    int r = nr_socket_getaddr(to, &addr_to);
    158    ASSERT_EQ(0, r);
    159    r = nr_socket_getaddr(from, &addr_from);
    160    ASSERT_EQ(0, r);
    161    printf("Connecting from %s to %s\n", addr_from.as_string,
    162           addr_to.as_string);
    163    r = nr_socket_connect(from, &addr_to);
    164    ASSERT_EQ(0, r);
    165  }
    166 
    167  void Connect(nr_socket* from, nr_socket* to) {
    168    test_utils_->SyncDispatchToSTS(
    169        WrapRunnable(this, &MultiTcpSocketTest::Connect_s, from, to));
    170  }
    171 
    172  void ConnectSo_s(nr_socket* so1, nr_socket* so2) {
    173    nr_transport_addr addr_so1;
    174    nr_transport_addr addr_so2;
    175    int r = nr_socket_getaddr(so1, &addr_so1);
    176    ASSERT_EQ(0, r);
    177    r = nr_socket_getaddr(so2, &addr_so2);
    178    ASSERT_EQ(0, r);
    179    printf("Connecting SO %s <-> %s\n", addr_so1.as_string, addr_so2.as_string);
    180    r = nr_socket_connect(so1, &addr_so2);
    181    ASSERT_EQ(0, r);
    182    r = nr_socket_connect(so2, &addr_so1);
    183    ASSERT_EQ(0, r);
    184  }
    185 
    186  void ConnectSo(nr_socket* from, nr_socket* to) {
    187    test_utils_->SyncDispatchToSTS(
    188        WrapRunnable(this, &MultiTcpSocketTest::ConnectSo_s, from, to));
    189  }
    190 
    191  void SendDataToAddress_s(nr_socket* from, nr_transport_addr* to,
    192                           const char* data, size_t len) {
    193    nr_transport_addr addr_from;
    194 
    195    int r = nr_socket_getaddr(from, &addr_from);
    196    ASSERT_EQ(0, r);
    197    printf("Sending %lu bytes %s -> %s\n", (unsigned long)len,
    198           addr_from.as_string, to->as_string);
    199    r = nr_socket_sendto(from, data, len, 0, to);
    200    ASSERT_EQ(0, r);
    201  }
    202 
    203  void SendData(nr_socket* from, nr_transport_addr* to, const char* data,
    204                size_t len) {
    205    test_utils_->SyncDispatchToSTS(WrapRunnable(
    206        this, &MultiTcpSocketTest::SendDataToAddress_s, from, to, data, len));
    207  }
    208 
    209  void SendDataToSocket_s(nr_socket* from, nr_socket* to, const char* data,
    210                          size_t len) {
    211    nr_transport_addr addr_to;
    212 
    213    int r = nr_socket_getaddr(to, &addr_to);
    214    ASSERT_EQ(0, r);
    215    SendDataToAddress_s(from, &addr_to, data, len);
    216  }
    217 
    218  void SendData(nr_socket* from, nr_socket* to, const char* data, size_t len) {
    219    test_utils_->SyncDispatchToSTS(WrapRunnable(
    220        this, &MultiTcpSocketTest::SendDataToSocket_s, from, to, data, len));
    221  }
    222 
    223  void RecvDataFromAddress_s(nr_transport_addr* expected_from,
    224                             nr_socket* sent_to, const char* expected_data,
    225                             size_t expected_len) {
    226    SetReadable(false);
    227    size_t buflen = expected_len ? expected_len + 1 : 100;
    228    char received_data[buflen];
    229    nr_transport_addr addr_to;
    230    nr_transport_addr retaddr;
    231    size_t retlen;
    232 
    233    int r = nr_socket_getaddr(sent_to, &addr_to);
    234    ASSERT_EQ(0, r);
    235    printf("Receiving %lu bytes %s <- %s\n", (unsigned long)expected_len,
    236           addr_to.as_string, expected_from->as_string);
    237    r = nr_socket_recvfrom(sent_to, received_data, buflen, &retlen, 0,
    238                           &retaddr);
    239    ASSERT_EQ(0, r);
    240    r = nr_transport_addr_cmp(&retaddr, expected_from,
    241                              NR_TRANSPORT_ADDR_CMP_MODE_ALL);
    242    ASSERT_EQ(0, r);
    243    // expected_len == 0 means we just expected some data
    244    if (expected_len == 0) {
    245      ASSERT_GT(retlen, 0U);
    246    } else {
    247      ASSERT_EQ(expected_len, retlen);
    248      r = memcmp(expected_data, received_data, retlen);
    249      ASSERT_EQ(0, r);
    250    }
    251  }
    252 
    253  void RecvData(nr_transport_addr* expected_from, nr_socket* sent_to,
    254                const char* expected_data = nullptr, size_t expected_len = 0) {
    255    ASSERT_TRUE_WAIT(IsReadable(), 1000);
    256    test_utils_->SyncDispatchToSTS(
    257        WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromAddress_s,
    258                     expected_from, sent_to, expected_data, expected_len));
    259  }
    260 
    261  void RecvDataFromSocket_s(nr_socket* expected_from, nr_socket* sent_to,
    262                            const char* expected_data, size_t expected_len) {
    263    nr_transport_addr addr_from;
    264 
    265    int r = nr_socket_getaddr(expected_from, &addr_from);
    266    ASSERT_EQ(0, r);
    267 
    268    RecvDataFromAddress_s(&addr_from, sent_to, expected_data, expected_len);
    269  }
    270 
    271  void RecvData(nr_socket* expected_from, nr_socket* sent_to,
    272                const char* expected_data, size_t expected_len) {
    273    ASSERT_TRUE_WAIT(IsReadable(), 1000);
    274    test_utils_->SyncDispatchToSTS(
    275        WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromSocket_s,
    276                     expected_from, sent_to, expected_data, expected_len));
    277  }
    278 
    279  void RecvDataFailed_s(nr_socket* sent_to, size_t expected_len,
    280                        int expected_err) {
    281    SetReadable(false);
    282    char received_data[expected_len + 1];
    283    nr_transport_addr addr_to;
    284    nr_transport_addr retaddr;
    285    size_t retlen;
    286 
    287    int r = nr_socket_getaddr(sent_to, &addr_to);
    288    ASSERT_EQ(0, r);
    289    r = nr_socket_recvfrom(sent_to, received_data, expected_len + 1, &retlen, 0,
    290                           &retaddr);
    291    ASSERT_EQ(expected_err, r) << "Expecting receive failure " << expected_err
    292                               << " on " << addr_to.as_string;
    293  }
    294 
    295  void RecvDataFailed(nr_socket* sent_to, size_t expected_len,
    296                      int expected_err) {
    297    ASSERT_TRUE_WAIT(IsReadable(), 1000);
    298    test_utils_->SyncDispatchToSTS(
    299        WrapRunnable(this, &MultiTcpSocketTest::RecvDataFailed_s, sent_to,
    300                     expected_len, expected_err));
    301  }
    302 
    303  void TransferData(nr_socket* from, nr_socket* to, const char* data,
    304                    size_t len) {
    305    SendData(from, to, data, len);
    306    RecvData(from, to, data, len);
    307  }
    308 
    309 protected:
    310  bool IsReadable() const { return readable; }
    311  void SetReadable(bool r) { readable = r; }
    312  std::vector<nr_socket*> socks;
    313  Atomic<bool> readable;
    314  RefPtr<NrIceCtx> ice_ctx_;
    315 };
    316 }  // namespace
    317 
    318 TEST_F(MultiTcpSocketTest, TestListen) {
    319  socks[0] = Create(TCP_TYPE_PASSIVE);
    320  Listen(socks[0]);
    321 }
    322 
    323 TEST_F(MultiTcpSocketTest, TestConnect) {
    324  socks[0] = Create(TCP_TYPE_PASSIVE);
    325  socks[1] = Create(TCP_TYPE_ACTIVE);
    326  socks[2] = Create(TCP_TYPE_ACTIVE);
    327  Listen(socks[0]);
    328  Connect(socks[1], socks[0]);
    329  Connect(socks[2], socks[0]);
    330 }
    331 
    332 TEST_F(MultiTcpSocketTest, TestTransmit) {
    333  const char data[] = "TestTransmit";
    334  socks[0] = Create(TCP_TYPE_ACTIVE);
    335  socks[1] = Create(TCP_TYPE_PASSIVE);
    336  Listen(socks[1]);
    337  Connect(socks[0], socks[1]);
    338 
    339  TransferData(socks[0], socks[1], data, sizeof(data));
    340  TransferData(socks[1], socks[0], data, sizeof(data));
    341 }
    342 
    343 TEST_F(MultiTcpSocketTest, TestClosePassive) {
    344  const char data[] = "TestClosePassive";
    345  socks[0] = Create(TCP_TYPE_ACTIVE);
    346  socks[1] = Create(TCP_TYPE_PASSIVE);
    347  Listen(socks[1]);
    348  Connect(socks[0], socks[1]);
    349 
    350  TransferData(socks[0], socks[1], data, sizeof(data));
    351  TransferData(socks[1], socks[0], data, sizeof(data));
    352 
    353  /* We have to destroy as only that calls PR_Close() */
    354  std::cerr << "Destructing socket" << std::endl;
    355  Destroy(socks[1]);
    356 
    357  RecvDataFailed(socks[0], sizeof(data), R_EOD);
    358 
    359  socks[1] = nullptr;
    360 }
    361 
    362 TEST_F(MultiTcpSocketTest, TestCloseActive) {
    363  const char data[] = "TestCloseActive";
    364  socks[0] = Create(TCP_TYPE_ACTIVE);
    365  socks[1] = Create(TCP_TYPE_PASSIVE);
    366  Listen(socks[1]);
    367  Connect(socks[0], socks[1]);
    368 
    369  TransferData(socks[0], socks[1], data, sizeof(data));
    370  TransferData(socks[1], socks[0], data, sizeof(data));
    371 
    372  /* We have to destroy as only that calls PR_Close() */
    373  std::cerr << "Destructing socket" << std::endl;
    374  Destroy(socks[0]);
    375 
    376  RecvDataFailed(socks[1], sizeof(data), R_EOD);
    377 
    378  socks[0] = nullptr;
    379 }
    380 
    381 TEST_F(MultiTcpSocketTest, TestTwoSendsBeforeReceives) {
    382  const char data1[] = "TestTwoSendsBeforeReceives";
    383  const char data2[] = "2nd data";
    384  socks[0] = Create(TCP_TYPE_ACTIVE);
    385  socks[1] = Create(TCP_TYPE_PASSIVE);
    386  Listen(socks[1]);
    387  Connect(socks[0], socks[1]);
    388 
    389  SendData(socks[0], socks[1], data1, sizeof(data1));
    390  SendData(socks[0], socks[1], data2, sizeof(data2));
    391  RecvData(socks[0], socks[1], data1, sizeof(data1));
    392  /* ICE TCP framing turns TCP effectively into datagram mode */
    393  RecvData(socks[0], socks[1], data2, sizeof(data2));
    394 }
    395 
    396 TEST_F(MultiTcpSocketTest, TestTwoActiveBidirectionalTransmit) {
    397  const char data1[] = "TestTwoActiveBidirectionalTransmit";
    398  const char data2[] = "ReplyToTheFirstSocket";
    399  const char data3[] = "TestMessageFromTheSecondSocket";
    400  const char data4[] = "ThisIsAReplyToTheSecondSocket";
    401  socks[0] = Create(TCP_TYPE_PASSIVE);
    402  socks[1] = Create(TCP_TYPE_ACTIVE);
    403  socks[2] = Create(TCP_TYPE_ACTIVE);
    404  Listen(socks[0]);
    405  Connect(socks[1], socks[0]);
    406  Connect(socks[2], socks[0]);
    407 
    408  TransferData(socks[1], socks[0], data1, sizeof(data1));
    409  TransferData(socks[0], socks[1], data2, sizeof(data2));
    410  TransferData(socks[2], socks[0], data3, sizeof(data3));
    411  TransferData(socks[0], socks[2], data4, sizeof(data4));
    412 }
    413 
    414 TEST_F(MultiTcpSocketTest, TestTwoPassiveBidirectionalTransmit) {
    415  const char data1[] = "TestTwoPassiveBidirectionalTransmit";
    416  const char data2[] = "FirstReply";
    417  const char data3[] = "TestTwoPassiveBidirectionalTransmitToTheSecondSock";
    418  const char data4[] = "SecondReply";
    419  socks[0] = Create(TCP_TYPE_PASSIVE);
    420  socks[1] = Create(TCP_TYPE_PASSIVE);
    421  socks[2] = Create(TCP_TYPE_ACTIVE);
    422  Listen(socks[0]);
    423  Listen(socks[1]);
    424  Connect(socks[2], socks[0]);
    425  Connect(socks[2], socks[1]);
    426 
    427  TransferData(socks[2], socks[0], data1, sizeof(data1));
    428  TransferData(socks[0], socks[2], data2, sizeof(data2));
    429  TransferData(socks[2], socks[1], data3, sizeof(data3));
    430  TransferData(socks[1], socks[2], data4, sizeof(data4));
    431 }
    432 
    433 TEST_F(MultiTcpSocketTest, TestActivePassiveWithStunServerMockup) {
    434  /* Fake STUN message able to pass the nr_is_stun_msg check
    435     used in nr_socket_buffered_stun */
    436  const char stunMessage[] = {'\x00', '\x01', '\x00', '\x04', '\x21', '\x12',
    437                              '\xa4', '\x42', '\x00', '\x00', '\x00', '\x00',
    438                              '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
    439                              '\x00', '\x00', '\x1c', '\xed', '\xca', '\xfe'};
    440  const char data[] = "TestActivePassiveWithStunServerMockup";
    441 
    442  nr_transport_addr stun_srv_addr;
    443  std::string stun_addr;
    444  uint16_t stun_port;
    445  stun_addr = TestStunTcpServer::GetInstance(AF_INET)->addr();
    446  stun_port = TestStunTcpServer::GetInstance(AF_INET)->port();
    447  int r = nr_str_port_to_transport_addr(stun_addr.c_str(), stun_port,
    448                                        IPPROTO_TCP, &stun_srv_addr);
    449  ASSERT_EQ(0, r);
    450 
    451  socks[0] = Create(TCP_TYPE_PASSIVE, stun_addr, stun_port);
    452  Listen(socks[0]);
    453  socks[1] = Create(TCP_TYPE_ACTIVE, stun_addr, stun_port);
    454 
    455  /* Send a fake STUN request and expect a STUN error response */
    456  SendData(socks[0], &stun_srv_addr, stunMessage, sizeof(stunMessage));
    457  RecvData(&stun_srv_addr, socks[0]);
    458 
    459  Connect(socks[1], socks[0]);
    460  TransferData(socks[1], socks[0], data, sizeof(data));
    461  TransferData(socks[0], socks[1], data, sizeof(data));
    462 }
    463 
    464 TEST_F(MultiTcpSocketTest, TestConnectTwoSo) {
    465  socks[0] = Create(TCP_TYPE_SO);
    466  socks[1] = Create(TCP_TYPE_SO);
    467  ConnectSo(socks[0], socks[1]);
    468 }
    469 
    470 // test works on localhost only with delay applied:
    471 //   tc qdisc add dev lo root netem delay 5ms
    472 TEST_F(MultiTcpSocketTest, DISABLED_TestTwoSoBidirectionalTransmit) {
    473  const char data[] = "TestTwoSoBidirectionalTransmit";
    474  socks[0] = Create(TCP_TYPE_SO);
    475  socks[1] = Create(TCP_TYPE_SO);
    476  ConnectSo(socks[0], socks[1]);
    477  TransferData(socks[0], socks[1], data, sizeof(data));
    478  TransferData(socks[1], socks[0], data, sizeof(data));
    479 }
    480 
    481 TEST_F(MultiTcpSocketTest, TestBigData) {
    482  char buf1[2048];
    483  char buf2[1024];
    484 
    485  for (unsigned i = 0; i < sizeof(buf1); ++i) {
    486    buf1[i] = i & 0xff;
    487  }
    488  for (unsigned i = 0; i < sizeof(buf2); ++i) {
    489    buf2[i] = (i + 0x80) & 0xff;
    490  }
    491  socks[0] = Create(TCP_TYPE_ACTIVE);
    492  socks[1] = Create(TCP_TYPE_PASSIVE);
    493  Listen(socks[1]);
    494  Connect(socks[0], socks[1]);
    495 
    496  TransferData(socks[0], socks[1], buf1, sizeof(buf1));
    497  TransferData(socks[0], socks[1], buf2, sizeof(buf2));
    498  // opposite dir
    499  SendData(socks[1], socks[0], buf2, sizeof(buf2));
    500  SendData(socks[1], socks[0], buf1, sizeof(buf1));
    501  RecvData(socks[1], socks[0], buf2, sizeof(buf2));
    502  RecvData(socks[1], socks[0], buf1, sizeof(buf1));
    503 }