media/mtransport/test/transport_unittests.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 // Original author: ekr@rtfm.com
     9 #include <iostream>
    10 #include <string>
    11 #include <map>
    13 #include "sigslot.h"
    15 #include "logging.h"
    16 #include "nspr.h"
    17 #include "nss.h"
    18 #include "ssl.h"
    20 #include "nsThreadUtils.h"
    21 #include "nsXPCOM.h"
    23 #include "dtlsidentity.h"
    24 #include "nricectx.h"
    25 #include "nricemediastream.h"
    26 #include "transportflow.h"
    27 #include "transportlayer.h"
    28 #include "transportlayerdtls.h"
    29 #include "transportlayerice.h"
    30 #include "transportlayerlog.h"
    31 #include "transportlayerloopback.h"
    33 #include "mtransport_test_utils.h"
    34 #include "runnable_utils.h"
    36 #define GTEST_HAS_RTTI 0
    37 #include "gtest/gtest.h"
    38 #include "gtest_utils.h"
    40 using namespace mozilla;
    41 MOZ_MTLOG_MODULE("mtransport")
    43 MtransportTestUtils *test_utils;
    45 // Layer class which can't be initialized.
    46 class TransportLayerDummy : public TransportLayer {
    47  public:
    48   TransportLayerDummy(bool allow_init, bool *destroyed)
    49       : allow_init_(allow_init),
    50         destroyed_(destroyed) {
    51     *destroyed_ = false;
    52   }
    54   virtual ~TransportLayerDummy() {
    55     *destroyed_ = true;
    56   }
    58   virtual nsresult InitInternal() {
    59     return allow_init_ ? NS_OK : NS_ERROR_FAILURE;
    60   }
    62   virtual TransportResult SendPacket(const unsigned char *data, size_t len) {
    63     MOZ_CRASH();  // Should never be called.
    64     return 0;
    65   }
    67   TRANSPORT_LAYER_ID("lossy")
    69  private:
    70   bool allow_init_;
    71   bool *destroyed_;
    72 };
    74 // Class to simulate various kinds of network lossage
    75 class TransportLayerLossy : public TransportLayer {
    76  public:
    77   TransportLayerLossy() : loss_mask_(0), packet_(0) {}
    78   ~TransportLayerLossy () {}
    80   virtual TransportResult SendPacket(const unsigned char *data, size_t len) {
    81     MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << len << ")");
    83     if (loss_mask_ & (1 << (packet_ % 32))) {
    84       MOZ_MTLOG(ML_NOTICE, "Dropping packet");
    85       ++packet_;
    86       return len;
    87     }
    89     ++packet_;
    91     return downward_->SendPacket(data, len);
    92   }
    94   void SetLoss(uint32_t packet) {
    95     loss_mask_ |= (1 << (packet & 32));
    96   }
    98   void StateChange(TransportLayer *layer, State state) {
    99     TL_SET_STATE(state);
   100   }
   102   void PacketReceived(TransportLayer *layer, const unsigned char *data,
   103                       size_t len) {
   104     SignalPacketReceived(this, data, len);
   105   }
   107   TRANSPORT_LAYER_ID("lossy")
   109  protected:
   110   virtual void WasInserted() {
   111     downward_->SignalPacketReceived.
   112         connect(this,
   113                 &TransportLayerLossy::PacketReceived);
   114     downward_->SignalStateChange.
   115         connect(this,
   116                 &TransportLayerLossy::StateChange);
   118     TL_SET_STATE(downward_->state());
   119   }
   121  private:
   122   uint32_t loss_mask_;
   123   uint32_t packet_;
   124 };
   126 namespace {
   127 class TransportTestPeer : public sigslot::has_slots<> {
   128  public:
   129   TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name)
   130       : name_(name), target_(target),
   131         received_(0), flow_(new TransportFlow(name)),
   132         loopback_(new TransportLayerLoopback()),
   133         logging_(new TransportLayerLogging()),
   134         lossy_(new TransportLayerLossy()),
   135         dtls_(new TransportLayerDtls()),
   136         identity_(DtlsIdentity::Generate()),
   137         ice_ctx_(NrIceCtx::Create(name,
   138                                   name == "P2" ?
   139                                   TransportLayerDtls::CLIENT :
   140                                   TransportLayerDtls::SERVER)),
   141         streams_(), candidates_(),
   142         peer_(nullptr),
   143         gathering_complete_(false)
   144  {
   145     std::vector<NrIceStunServer> stun_servers;
   146     ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(
   147         std::string((char *)"216.93.246.14"), 3478));
   148     stun_servers.push_back(*server);
   149     EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
   151     dtls_->SetIdentity(identity_);
   152     dtls_->SetRole(name == "P2" ?
   153                    TransportLayerDtls::CLIENT :
   154                    TransportLayerDtls::SERVER);
   156     nsresult res = identity_->ComputeFingerprint("sha-1",
   157                                              fingerprint_,
   158                                              sizeof(fingerprint_),
   159                                              &fingerprint_len_);
   160     EXPECT_TRUE(NS_SUCCEEDED(res));
   161     EXPECT_EQ(20u, fingerprint_len_);
   162   }
   164   ~TransportTestPeer() {
   165     test_utils->sts_target()->Dispatch(
   166       WrapRunnable(this, &TransportTestPeer::DestroyFlow),
   167       NS_DISPATCH_SYNC);
   168   }
   171   void DestroyFlow() {
   172     if (flow_) {
   173       loopback_->Disconnect();
   174       flow_ = nullptr;
   175     }
   176     ice_ctx_ = nullptr;
   177   }
   179   void DisconnectDestroyFlow() {
   180     loopback_->Disconnect();
   181     disconnect_all();  // Disconnect from the signals;
   182      flow_ = nullptr;
   183   }
   185   void SetDtlsAllowAll() {
   186     nsresult res = dtls_->SetVerificationAllowAll();
   187     ASSERT_TRUE(NS_SUCCEEDED(res));
   188   }
   190   void SetDtlsPeer(TransportTestPeer *peer, int digests, unsigned int damage) {
   191     unsigned int mask = 1;
   193     for (int i=0; i<digests; i++) {
   194       unsigned char fingerprint_to_set[TransportLayerDtls::kMaxDigestLength];
   196       memcpy(fingerprint_to_set,
   197              peer->fingerprint_,
   198              peer->fingerprint_len_);
   199       if (damage & mask)
   200         fingerprint_to_set[0]++;
   202       nsresult res = dtls_->SetVerificationDigest(
   203           "sha-1",
   204           fingerprint_to_set,
   205           peer->fingerprint_len_);
   207       ASSERT_TRUE(NS_SUCCEEDED(res));
   209       mask <<= 1;
   210     }
   211   }
   214   void ConnectSocket_s(TransportTestPeer *peer) {
   215     nsresult res;
   216     res = loopback_->Init();
   217     ASSERT_EQ((nsresult)NS_OK, res);
   219     loopback_->Connect(peer->loopback_);
   221     ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
   222     ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(logging_));
   223     ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(lossy_));
   224     ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(dtls_));
   226     flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
   227   }
   229   void ConnectSocket(TransportTestPeer *peer) {
   230     RUN_ON_THREAD(test_utils->sts_target(),
   231                   WrapRunnable(this, & TransportTestPeer::ConnectSocket_s,
   232                                peer),
   233                   NS_DISPATCH_SYNC);
   234   }
   236   void InitIce() {
   237     nsresult res;
   239     // Attach our slots
   240     ice_ctx_->SignalGatheringStateChange.
   241         connect(this, &TransportTestPeer::GatheringStateChange);
   243     char name[100];
   244     snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
   245              (int)streams_.size());
   247     // Create the media stream
   248     mozilla::RefPtr<NrIceMediaStream> stream =
   249         ice_ctx_->CreateStream(static_cast<char *>(name), 1);
   250     ASSERT_TRUE(stream != nullptr);
   251     streams_.push_back(stream);
   253     // Listen for candidates
   254     stream->SignalCandidate.
   255         connect(this, &TransportTestPeer::GotCandidate);
   257     // Create the transport layer
   258     ice_ = new TransportLayerIce(name, ice_ctx_, stream, 1);
   260     // Assemble the stack
   261     nsAutoPtr<std::queue<mozilla::TransportLayer *> > layers(
   262       new std::queue<mozilla::TransportLayer *>);
   263     layers->push(ice_);
   264     layers->push(dtls_);
   266     test_utils->sts_target()->Dispatch(
   267       WrapRunnableRet(flow_, &TransportFlow::PushLayers, layers, &res),
   268       NS_DISPATCH_SYNC);
   270     ASSERT_EQ((nsresult)NS_OK, res);
   272     // Listen for media events
   273     flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
   274     flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
   276     // Start gathering
   277     test_utils->sts_target()->Dispatch(
   278         WrapRunnableRet(ice_ctx_, &NrIceCtx::StartGathering, &res),
   279         NS_DISPATCH_SYNC);
   280     ASSERT_TRUE(NS_SUCCEEDED(res));
   281   }
   283   void ConnectIce(TransportTestPeer *peer) {
   284     peer_ = peer;
   286     // If gathering is already complete, push the candidates over
   287     if (gathering_complete_)
   288       GatheringComplete();
   289   }
   291   // New candidate
   292   void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
   293     std::cerr << "Got candidate " << candidate << std::endl;
   294     candidates_[stream->name()].push_back(candidate);
   295   }
   297   void GatheringStateChange(NrIceCtx* ctx,
   298                             NrIceCtx::GatheringState state) {
   299     (void)ctx;
   300     if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
   301       GatheringComplete();
   302     }
   303   }
   305   // Gathering complete, so send our candidates and start
   306   // connecting on the other peer.
   307   void GatheringComplete() {
   308     nsresult res;
   310     // Don't send to the other side
   311     if (!peer_) {
   312       gathering_complete_ = true;
   313       return;
   314     }
   316     // First send attributes
   317     test_utils->sts_target()->Dispatch(
   318       WrapRunnableRet(peer_->ice_ctx_,
   319                       &NrIceCtx::ParseGlobalAttributes,
   320                       ice_ctx_->GetGlobalAttributes(), &res),
   321       NS_DISPATCH_SYNC);
   322     ASSERT_TRUE(NS_SUCCEEDED(res));
   324     for (size_t i=0; i<streams_.size(); ++i) {
   325       test_utils->sts_target()->Dispatch(
   326         WrapRunnableRet(peer_->streams_[i], &NrIceMediaStream::ParseAttributes,
   327                         candidates_[streams_[i]->name()], &res), NS_DISPATCH_SYNC);
   329       ASSERT_TRUE(NS_SUCCEEDED(res));
   330     }
   332     // Start checks on the other peer.
   333     test_utils->sts_target()->Dispatch(
   334       WrapRunnableRet(peer_->ice_ctx_, &NrIceCtx::StartChecks, &res),
   335       NS_DISPATCH_SYNC);
   336     ASSERT_TRUE(NS_SUCCEEDED(res));
   337   }
   339   TransportResult SendPacket(const unsigned char* data, size_t len) {
   340     TransportResult ret;
   341     test_utils->sts_target()->Dispatch(
   342       WrapRunnableRet(flow_, &TransportFlow::SendPacket, data, len, &ret),
   343       NS_DISPATCH_SYNC);
   345     return ret;
   346   }
   349   void StateChanged(TransportFlow *flow, TransportLayer::State state) {
   350     if (state == TransportLayer::TS_OPEN) {
   351       std::cerr << "Now connected" << std::endl;
   352     }
   353   }
   355   void PacketReceived(TransportFlow * flow, const unsigned char* data,
   356                       size_t len) {
   357     std::cerr << "Received " << len << " bytes" << std::endl;
   358     ++received_;
   359   }
   361   void SetLoss(uint32_t loss) {
   362     lossy_->SetLoss(loss);
   363   }
   365   TransportLayer::State state() {
   366     TransportLayer::State tstate;
   368     RUN_ON_THREAD(test_utils->sts_target(),
   369                   WrapRunnableRet(flow_, &TransportFlow::state, &tstate),
   370                   NS_DISPATCH_SYNC);
   372     return tstate;
   373   }
   375   bool connected() {
   376     return state() == TransportLayer::TS_OPEN;
   377   }
   379   bool failed() {
   380     return state() == TransportLayer::TS_ERROR;
   381   }
   383   size_t received() { return received_; }
   385  private:
   386   std::string name_;
   387   nsCOMPtr<nsIEventTarget> target_;
   388   size_t received_;
   389     mozilla::RefPtr<TransportFlow> flow_;
   390   TransportLayerLoopback *loopback_;
   391   TransportLayerLogging *logging_;
   392   TransportLayerLossy *lossy_;
   393   TransportLayerDtls *dtls_;
   394   TransportLayerIce *ice_;
   395   mozilla::RefPtr<DtlsIdentity> identity_;
   396   mozilla::RefPtr<NrIceCtx> ice_ctx_;
   397   std::vector<mozilla::RefPtr<NrIceMediaStream> > streams_;
   398   std::map<std::string, std::vector<std::string> > candidates_;
   399   TransportTestPeer *peer_;
   400   bool gathering_complete_;
   401   unsigned char fingerprint_[TransportLayerDtls::kMaxDigestLength];
   402   size_t fingerprint_len_;
   403 };
   406 class TransportTest : public ::testing::Test {
   407  public:
   408   TransportTest() {
   409     fds_[0] = nullptr;
   410     fds_[1] = nullptr;
   411   }
   413   ~TransportTest() {
   414     delete p1_;
   415     delete p2_;
   417     //    Can't detach these
   418     //    PR_Close(fds_[0]);
   419     //    PR_Close(fds_[1]);
   420   }
   422   void DestroyPeerFlows() {
   423     p1_->DisconnectDestroyFlow();
   424     p2_->DisconnectDestroyFlow();
   425   }
   427   void SetUp() {
   428     nsresult rv;
   429     target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   430     ASSERT_TRUE(NS_SUCCEEDED(rv));
   432     p1_ = new TransportTestPeer(target_, "P1");
   433     p2_ = new TransportTestPeer(target_, "P2");
   434   }
   436   void SetDtlsPeer(int digests = 1, unsigned int damage = 0) {
   437     p1_->SetDtlsPeer(p2_, digests, damage);
   438     p2_->SetDtlsPeer(p1_, digests, damage);
   439   }
   441   void SetDtlsAllowAll() {
   442     p1_->SetDtlsAllowAll();
   443     p2_->SetDtlsAllowAll();
   444   }
   446   void ConnectSocket() {
   447     test_utils->sts_target()->Dispatch(
   448       WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_),
   449       NS_DISPATCH_SYNC);
   450     test_utils->sts_target()->Dispatch(
   451       WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_),
   452       NS_DISPATCH_SYNC);
   454     ASSERT_TRUE_WAIT(p1_->connected(), 10000);
   455     ASSERT_TRUE_WAIT(p2_->connected(), 10000);
   456   }
   458   void ConnectSocketExpectFail() {
   459     test_utils->sts_target()->Dispatch(
   460       WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_),
   461       NS_DISPATCH_SYNC);
   462     test_utils->sts_target()->Dispatch(
   463       WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_),
   464       NS_DISPATCH_SYNC);
   465     ASSERT_TRUE_WAIT(p1_->failed(), 10000);
   466     ASSERT_TRUE_WAIT(p2_->failed(), 10000);
   467   }
   469   void InitIce() {
   470     p1_->InitIce();
   471     p2_->InitIce();
   472   }
   474   void ConnectIce() {
   475     p1_->InitIce();
   476     p2_->InitIce();
   477     p1_->ConnectIce(p2_);
   478     p2_->ConnectIce(p1_);
   479     ASSERT_TRUE_WAIT(p1_->connected(), 10000);
   480     ASSERT_TRUE_WAIT(p2_->connected(), 10000);
   481   }
   483   void TransferTest(size_t count) {
   484     unsigned char buf[1000];
   486     for (size_t i= 0; i<count; ++i) {
   487       memset(buf, count & 0xff, sizeof(buf));
   488       TransportResult rv = p1_->SendPacket(buf, sizeof(buf));
   489       ASSERT_TRUE(rv > 0);
   490     }
   492     std::cerr << "Received == " << p2_->received() << std::endl;
   493     ASSERT_TRUE_WAIT(count == p2_->received(), 10000);
   494   }
   496  protected:
   497   PRFileDesc *fds_[2];
   498   TransportTestPeer *p1_;
   499   TransportTestPeer *p2_;
   500   nsCOMPtr<nsIEventTarget> target_;
   501 };
   504 TEST_F(TransportTest, TestNoDtlsVerificationSettings) {
   505   ConnectSocketExpectFail();
   506 }
   508 TEST_F(TransportTest, TestConnect) {
   509   SetDtlsPeer();
   510   ConnectSocket();
   511 }
   513 TEST_F(TransportTest, TestConnectDestroyFlowsMainThread) {
   514   SetDtlsPeer();
   515   ConnectSocket();
   516   DestroyPeerFlows();
   517 }
   519 TEST_F(TransportTest, TestConnectAllowAll) {
   520   SetDtlsAllowAll();
   521   ConnectSocket();
   522 }
   524 TEST_F(TransportTest, TestConnectBadDigest) {
   525   SetDtlsPeer(1, 1);
   527   ConnectSocketExpectFail();
   528 }
   530 TEST_F(TransportTest, TestConnectTwoDigests) {
   531   SetDtlsPeer(2, 0);
   533   ConnectSocket();
   534 }
   536 TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) {
   537   SetDtlsPeer(2, 1);
   539   ConnectSocketExpectFail();
   540 }
   542 TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) {
   543   SetDtlsPeer(2, 2);
   545   ConnectSocketExpectFail();
   546 }
   548 TEST_F(TransportTest, TestConnectTwoDigestsBothBad) {
   549   SetDtlsPeer(2, 3);
   551   ConnectSocketExpectFail();
   552 }
   554 TEST_F(TransportTest, TestTransfer) {
   555   SetDtlsPeer();
   556   ConnectSocket();
   557   TransferTest(1);
   558 }
   560 TEST_F(TransportTest, TestConnectLoseFirst) {
   561   SetDtlsPeer();
   562   p1_->SetLoss(0);
   563   ConnectSocket();
   564   TransferTest(1);
   565 }
   567 TEST_F(TransportTest, TestConnectIce) {
   568   SetDtlsPeer();
   569   ConnectIce();
   570 }
   572 TEST_F(TransportTest, TestTransferIce) {
   573   SetDtlsPeer();
   574   ConnectIce();
   575   TransferTest(1);
   576 }
   578 TEST(PushTests, LayerFail) {
   579   TransportFlow flow;
   580   nsresult rv;
   581   bool destroyed1, destroyed2;
   583   rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1));
   584   ASSERT_TRUE(NS_SUCCEEDED(rv));
   586   rv = flow.PushLayer(new TransportLayerDummy(false, &destroyed2));
   587   ASSERT_TRUE(NS_FAILED(rv));
   589   ASSERT_EQ(TransportLayer::TS_ERROR, flow.state());
   590   ASSERT_EQ(true, destroyed1);
   591   ASSERT_EQ(true, destroyed2);
   593   rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1));
   594   ASSERT_TRUE(NS_FAILED(rv));
   595   ASSERT_EQ(true, destroyed1);
   596 }
   599 TEST(PushTests, LayersFail) {
   600   TransportFlow flow;
   601   nsresult rv;
   602   bool destroyed1, destroyed2, destroyed3;
   604   rv = flow.PushLayer(new TransportLayerDummy(true, &destroyed1));
   605   ASSERT_TRUE(NS_SUCCEEDED(rv));
   607   nsAutoPtr<std::queue<TransportLayer *> > layers(
   608       new std::queue<TransportLayer *>());
   610   layers->push(new TransportLayerDummy(true, &destroyed2));
   611   layers->push(new TransportLayerDummy(false, &destroyed3));
   613   rv = flow.PushLayers(layers);
   614   ASSERT_TRUE(NS_FAILED(rv));
   616   ASSERT_EQ(TransportLayer::TS_ERROR, flow.state());
   617   ASSERT_EQ(true, destroyed1);
   618   ASSERT_EQ(true, destroyed2);
   619   ASSERT_EQ(true, destroyed3);
   621   layers = new std::queue<TransportLayer *>();
   622   layers->push(new TransportLayerDummy(true, &destroyed2));
   623   layers->push(new TransportLayerDummy(true, &destroyed3));
   624   rv = flow.PushLayers(layers);
   626   ASSERT_TRUE(NS_FAILED(rv));
   627   ASSERT_EQ(true, destroyed2);
   628   ASSERT_EQ(true, destroyed3);
   629 }
   631 }  // end namespace
   633 int main(int argc, char **argv)
   634 {
   635   test_utils = new MtransportTestUtils();
   637   NSS_NoDB_Init(nullptr);
   638   NSS_SetDomesticPolicy();
   639   // Start the tests
   640   ::testing::InitGoogleTest(&argc, argv);
   642   int rv = RUN_ALL_TESTS();
   643   delete test_utils;
   644   return rv;
   645 }

mercurial