tor-browser

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

TestBind.cpp (5490B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "TestCommon.h"
      6 #include "gtest/gtest.h"
      7 #include "mozilla/gtest/MozAssertions.h"
      8 #include "nsISocketTransportService.h"
      9 #include "nsISocketTransport.h"
     10 #include "nsIServerSocket.h"
     11 #include "nsIAsyncInputStream.h"
     12 #include "mozilla/net/DNS.h"
     13 #include "prerror.h"
     14 #include "../../base/nsSocketTransportService2.h"
     15 #include "nsComponentManagerUtils.h"
     16 #include "nsServiceManagerUtils.h"
     17 
     18 using namespace mozilla::net;
     19 using namespace mozilla;
     20 
     21 class ServerListener : public nsIServerSocketListener {
     22 public:
     23  NS_DECL_ISUPPORTS
     24  NS_DECL_NSISERVERSOCKETLISTENER
     25 
     26  explicit ServerListener(WaitForCondition* waiter);
     27 
     28  // Port that is got from server side will be store here.
     29  uint32_t mClientPort;
     30  bool mFailed;
     31  RefPtr<WaitForCondition> mWaiter;
     32 
     33 private:
     34  virtual ~ServerListener();
     35 };
     36 
     37 NS_IMPL_ISUPPORTS(ServerListener, nsIServerSocketListener)
     38 
     39 ServerListener::ServerListener(WaitForCondition* waiter)
     40    : mClientPort(-1), mFailed(false), mWaiter(waiter) {}
     41 
     42 ServerListener::~ServerListener() = default;
     43 
     44 NS_IMETHODIMP
     45 ServerListener::OnSocketAccepted(nsIServerSocket* aServ,
     46                                 nsISocketTransport* aTransport) {
     47  // Run on STS thread.
     48  NetAddr peerAddr;
     49  nsresult rv = aTransport->GetPeerAddr(&peerAddr);
     50  if (NS_FAILED(rv)) {
     51    mFailed = true;
     52    mWaiter->Notify();
     53    return NS_OK;
     54  }
     55  mClientPort = PR_ntohs(peerAddr.inet.port);
     56  mWaiter->Notify();
     57  return NS_OK;
     58 }
     59 
     60 NS_IMETHODIMP
     61 ServerListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus) {
     62  return NS_OK;
     63 }
     64 
     65 class ClientInputCallback : public nsIInputStreamCallback {
     66 public:
     67  NS_DECL_THREADSAFE_ISUPPORTS
     68  NS_DECL_NSIINPUTSTREAMCALLBACK
     69 
     70  explicit ClientInputCallback(WaitForCondition* waiter);
     71 
     72  bool mFailed;
     73  RefPtr<WaitForCondition> mWaiter;
     74 
     75 private:
     76  virtual ~ClientInputCallback();
     77 };
     78 
     79 NS_IMPL_ISUPPORTS(ClientInputCallback, nsIInputStreamCallback)
     80 
     81 ClientInputCallback::ClientInputCallback(WaitForCondition* waiter)
     82    : mFailed(false), mWaiter(waiter) {}
     83 
     84 ClientInputCallback::~ClientInputCallback() = default;
     85 
     86 NS_IMETHODIMP
     87 ClientInputCallback::OnInputStreamReady(nsIAsyncInputStream* aStream) {
     88  // Server doesn't send. That means if we are here, we probably have run into
     89  // an error.
     90  uint64_t avail;
     91  nsresult rv = aStream->Available(&avail);
     92  if (NS_FAILED(rv)) {
     93    mFailed = true;
     94  }
     95  mWaiter->Notify();
     96  return NS_OK;
     97 }
     98 
     99 TEST(TestBind, MainTest)
    100 {
    101  //
    102  // Server side.
    103  //
    104  nsCOMPtr<nsIServerSocket> server =
    105      do_CreateInstance("@mozilla.org/network/server-socket;1");
    106  ASSERT_TRUE(server);
    107 
    108  nsresult rv = server->Init(-1, true, -1);
    109  ASSERT_NS_SUCCEEDED(rv);
    110 
    111  int32_t serverPort;
    112  rv = server->GetPort(&serverPort);
    113  ASSERT_NS_SUCCEEDED(rv);
    114 
    115  RefPtr<WaitForCondition> waiter = new WaitForCondition();
    116 
    117  // Listening.
    118  RefPtr<ServerListener> serverListener = new ServerListener(waiter);
    119  rv = server->AsyncListen(serverListener);
    120  ASSERT_NS_SUCCEEDED(rv);
    121 
    122  //
    123  // Client side
    124  //
    125  uint32_t bindingPort = 20000;
    126  nsCOMPtr<nsISocketTransportService> service =
    127      do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
    128  ASSERT_NS_SUCCEEDED(rv);
    129 
    130  nsCOMPtr<nsIInputStream> inputStream;
    131  RefPtr<ClientInputCallback> clientCallback;
    132 
    133  auto* sts = gSocketTransportService;
    134  ASSERT_TRUE(sts);
    135  for (int32_t tried = 0; tried < 100; tried++) {
    136    NS_DispatchAndSpinEventLoopUntilComplete(
    137        "test"_ns, sts, NS_NewRunnableFunction("test", [&]() {
    138          nsCOMPtr<nsISocketTransport> client;
    139          rv = service->CreateTransport(nsTArray<nsCString>(), "127.0.0.1"_ns,
    140                                        serverPort, nullptr, nullptr,
    141                                        getter_AddRefs(client));
    142          ASSERT_NS_SUCCEEDED(rv);
    143 
    144          // Bind to a port. It's possible that we are binding to a port
    145          // that is currently in use. If we failed to bind, we try next
    146          // port.
    147          NetAddr bindingAddr;
    148          bindingAddr.inet.family = AF_INET;
    149          bindingAddr.inet.ip = 0;
    150          bindingAddr.inet.port = PR_htons(bindingPort);
    151          rv = client->Bind(&bindingAddr);
    152          ASSERT_NS_SUCCEEDED(rv);
    153 
    154          // Open IO streams, to make client SocketTransport connect to
    155          // server.
    156          clientCallback = new ClientInputCallback(waiter);
    157          rv = client->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
    158                                       getter_AddRefs(inputStream));
    159          ASSERT_NS_SUCCEEDED(rv);
    160 
    161          nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
    162              do_QueryInterface(inputStream);
    163          rv = asyncInputStream->AsyncWait(clientCallback, 0, 0, nullptr);
    164        }));
    165 
    166    // Wait for server's response or callback of input stream.
    167    waiter->Wait(1);
    168    if (clientCallback->mFailed) {
    169      // if client received error, we likely have bound a port that is
    170      // in use. we can try another port.
    171      bindingPort++;
    172    } else {
    173      // We are unlocked by server side, leave the loop and check
    174      // result.
    175      break;
    176    }
    177  }
    178 
    179  ASSERT_FALSE(serverListener->mFailed);
    180  ASSERT_EQ(serverListener->mClientPort, bindingPort);
    181 
    182  inputStream->Close();
    183  waiter->Wait(1);
    184  ASSERT_TRUE(clientCallback->mFailed);
    185 
    186  server->Close();
    187 }