media/mtransport/test/sctp_unittest.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.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // Original author: ekr@rtfm.com
michael@0 8
michael@0 9 #include <iostream>
michael@0 10 #include <string>
michael@0 11 #include <map>
michael@0 12
michael@0 13 #include "sigslot.h"
michael@0 14
michael@0 15 #include "logging.h"
michael@0 16 #include "nsNetCID.h"
michael@0 17 #include "nsITimer.h"
michael@0 18 #include "nsComponentManagerUtils.h"
michael@0 19 #include "nsThreadUtils.h"
michael@0 20 #include "nsXPCOM.h"
michael@0 21
michael@0 22 #include "transportflow.h"
michael@0 23 #include "transportlayer.h"
michael@0 24 #include "transportlayerloopback.h"
michael@0 25
michael@0 26 #include "mtransport_test_utils.h"
michael@0 27 #include "runnable_utils.h"
michael@0 28 #include "usrsctp.h"
michael@0 29
michael@0 30 #define GTEST_HAS_RTTI 0
michael@0 31 #include "gtest/gtest.h"
michael@0 32 #include "gtest_utils.h"
michael@0 33
michael@0 34
michael@0 35 using namespace mozilla;
michael@0 36
michael@0 37 MtransportTestUtils *test_utils;
michael@0 38 static bool sctp_logging = false;
michael@0 39 static int port_number = 5000;
michael@0 40
michael@0 41 namespace {
michael@0 42
michael@0 43 class TransportTestPeer;
michael@0 44
michael@0 45 class SendPeriodic : public nsITimerCallback {
michael@0 46 public:
michael@0 47 SendPeriodic(TransportTestPeer *peer, int to_send) :
michael@0 48 peer_(peer),
michael@0 49 to_send_(to_send) {}
michael@0 50 virtual ~SendPeriodic() {}
michael@0 51
michael@0 52 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 53 NS_DECL_NSITIMERCALLBACK
michael@0 54
michael@0 55 protected:
michael@0 56 TransportTestPeer *peer_;
michael@0 57 int to_send_;
michael@0 58 };
michael@0 59
michael@0 60 NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
michael@0 61
michael@0 62
michael@0 63 class TransportTestPeer : public sigslot::has_slots<> {
michael@0 64 public:
michael@0 65 TransportTestPeer(std::string name, int local_port, int remote_port)
michael@0 66 : name_(name), connected_(false),
michael@0 67 sent_(0), received_(0),
michael@0 68 flow_(new TransportFlow()),
michael@0 69 loopback_(new TransportLayerLoopback()),
michael@0 70 sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
michael@0 71 timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
michael@0 72 periodic_(nullptr) {
michael@0 73 std::cerr << "Creating TransportTestPeer; flow=" <<
michael@0 74 static_cast<void *>(flow_.get()) <<
michael@0 75 " local=" << local_port <<
michael@0 76 " remote=" << remote_port << std::endl;
michael@0 77
michael@0 78 usrsctp_register_address(static_cast<void *>(this));
michael@0 79 int r = usrsctp_set_non_blocking(sctp_, 1);
michael@0 80 EXPECT_GE(r, 0);
michael@0 81
michael@0 82 struct linger l;
michael@0 83 l.l_onoff = 1;
michael@0 84 l.l_linger = 0;
michael@0 85 r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
michael@0 86 (socklen_t)sizeof(l));
michael@0 87 EXPECT_GE(r, 0);
michael@0 88
michael@0 89 struct sctp_event subscription;
michael@0 90 memset(&subscription, 0, sizeof(subscription));
michael@0 91 subscription.se_assoc_id = SCTP_ALL_ASSOC;
michael@0 92 subscription.se_on = 1;
michael@0 93 subscription.se_type = SCTP_ASSOC_CHANGE;
michael@0 94 r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription,
michael@0 95 sizeof(subscription));
michael@0 96 EXPECT_GE(r, 0);
michael@0 97
michael@0 98 memset(&local_addr_, 0, sizeof(local_addr_));
michael@0 99 local_addr_.sconn_family = AF_CONN;
michael@0 100 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
michael@0 101 local_addr_.sconn_len = sizeof(struct sockaddr_conn);
michael@0 102 #endif
michael@0 103 local_addr_.sconn_port = htons(local_port);
michael@0 104 local_addr_.sconn_addr = static_cast<void *>(this);
michael@0 105
michael@0 106
michael@0 107 memset(&remote_addr_, 0, sizeof(remote_addr_));
michael@0 108 remote_addr_.sconn_family = AF_CONN;
michael@0 109 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
michael@0 110 remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
michael@0 111 #endif
michael@0 112 remote_addr_.sconn_port = htons(remote_port);
michael@0 113 remote_addr_.sconn_addr = static_cast<void *>(this);
michael@0 114
michael@0 115 nsresult res;
michael@0 116 res = loopback_->Init();
michael@0 117 EXPECT_EQ((nsresult)NS_OK, res);
michael@0 118 }
michael@0 119
michael@0 120 ~TransportTestPeer() {
michael@0 121 std::cerr << "Destroying sctp connection flow=" <<
michael@0 122 static_cast<void *>(flow_.get()) << std::endl;
michael@0 123 usrsctp_close(sctp_);
michael@0 124 usrsctp_deregister_address(static_cast<void *>(this));
michael@0 125
michael@0 126 test_utils->sts_target()->Dispatch(WrapRunnable(this,
michael@0 127 &TransportTestPeer::Disconnect_s),
michael@0 128 NS_DISPATCH_SYNC);
michael@0 129
michael@0 130 std::cerr << "~TransportTestPeer() completed" << std::endl;
michael@0 131 }
michael@0 132
michael@0 133 void ConnectSocket(TransportTestPeer *peer) {
michael@0 134 test_utils->sts_target()->Dispatch(WrapRunnable(
michael@0 135 this, &TransportTestPeer::ConnectSocket_s, peer),
michael@0 136 NS_DISPATCH_SYNC);
michael@0 137 }
michael@0 138
michael@0 139 void ConnectSocket_s(TransportTestPeer *peer) {
michael@0 140 loopback_->Connect(peer->loopback_);
michael@0 141
michael@0 142 ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
michael@0 143
michael@0 144 flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
michael@0 145
michael@0 146 // SCTP here!
michael@0 147 ASSERT_TRUE(sctp_);
michael@0 148 std::cerr << "Calling usrsctp_bind()" << std::endl;
michael@0 149 int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>(
michael@0 150 &local_addr_), sizeof(local_addr_));
michael@0 151 ASSERT_GE(0, r);
michael@0 152
michael@0 153 std::cerr << "Calling usrsctp_connect()" << std::endl;
michael@0 154 r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
michael@0 155 &remote_addr_), sizeof(remote_addr_));
michael@0 156 ASSERT_GE(0, r);
michael@0 157 }
michael@0 158
michael@0 159 void Disconnect_s() {
michael@0 160 if (flow_) {
michael@0 161 flow_ = nullptr;
michael@0 162 }
michael@0 163 }
michael@0 164
michael@0 165 void Disconnect() {
michael@0 166 loopback_->Disconnect();
michael@0 167 }
michael@0 168
michael@0 169
michael@0 170 void StartTransfer(size_t to_send) {
michael@0 171 periodic_ = new SendPeriodic(this, to_send);
michael@0 172 timer_->SetTarget(test_utils->sts_target());
michael@0 173 timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK);
michael@0 174 }
michael@0 175
michael@0 176 void SendOne() {
michael@0 177 unsigned char buf[100];
michael@0 178 memset(buf, sent_ & 0xff, sizeof(buf));
michael@0 179
michael@0 180 struct sctp_sndinfo info;
michael@0 181 info.snd_sid = 1;
michael@0 182 info.snd_flags = 0;
michael@0 183 info.snd_ppid = 50; // What the heck is this?
michael@0 184 info.snd_context = 0;
michael@0 185 info.snd_assoc_id = 0;
michael@0 186
michael@0 187 int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0,
michael@0 188 static_cast<void *>(&info),
michael@0 189 sizeof(info), SCTP_SENDV_SNDINFO, 0);
michael@0 190 ASSERT_TRUE(r >= 0);
michael@0 191 ASSERT_EQ(sizeof(buf), (size_t)r);
michael@0 192
michael@0 193 ++sent_;
michael@0 194 }
michael@0 195
michael@0 196 int sent() const { return sent_; }
michael@0 197 int received() const { return received_; }
michael@0 198 bool connected() const { return connected_; }
michael@0 199
michael@0 200 static TransportResult SendPacket_s(const unsigned char* data, size_t len,
michael@0 201 const mozilla::RefPtr<TransportFlow>& flow) {
michael@0 202 TransportResult res = flow->SendPacket(data, len);
michael@0 203 delete data; // we always allocate
michael@0 204 return res;
michael@0 205 }
michael@0 206
michael@0 207 TransportResult SendPacket(const unsigned char* data, size_t len) {
michael@0 208 unsigned char *buffer = new unsigned char[len];
michael@0 209 memcpy(buffer, data, len);
michael@0 210
michael@0 211 // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
michael@0 212 // from MainThread especially during shutdown (same as DataChannels).
michael@0 213 // RUN_ON_THREAD short-circuits if already on the STS thread, which is
michael@0 214 // normal for most transfers outside of connect() and close(). Passes
michael@0 215 // a refptr to flow_ to avoid any async deletion issues (since we can't
michael@0 216 // make 'this' into a refptr as it isn't refcounted)
michael@0 217 RUN_ON_THREAD(test_utils->sts_target(), WrapRunnableNM(
michael@0 218 &TransportTestPeer::SendPacket_s, buffer, len, flow_),
michael@0 219 NS_DISPATCH_NORMAL);
michael@0 220
michael@0 221 return 0;
michael@0 222 }
michael@0 223
michael@0 224 void PacketReceived(TransportFlow * flow, const unsigned char* data,
michael@0 225 size_t len) {
michael@0 226 std::cerr << "Received " << len << " bytes" << std::endl;
michael@0 227
michael@0 228 // Pass the data to SCTP
michael@0 229
michael@0 230 usrsctp_conninput(static_cast<void *>(this), data, len, 0);
michael@0 231 }
michael@0 232
michael@0 233 // Process SCTP notification
michael@0 234 void Notification(union sctp_notification *msg, size_t len) {
michael@0 235 ASSERT_EQ(msg->sn_header.sn_length, len);
michael@0 236
michael@0 237 if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
michael@0 238 struct sctp_assoc_change *change = &msg->sn_assoc_change;
michael@0 239
michael@0 240 if (change->sac_state == SCTP_COMM_UP) {
michael@0 241 std::cerr << "Connection up" << std::endl;
michael@0 242 SetConnected(true);
michael@0 243 } else {
michael@0 244 std::cerr << "Connection down" << std::endl;
michael@0 245 SetConnected(false);
michael@0 246 }
michael@0 247 }
michael@0 248 }
michael@0 249
michael@0 250 void SetConnected(bool state) {
michael@0 251 connected_ = state;
michael@0 252 }
michael@0 253
michael@0 254 static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) {
michael@0 255 TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr);
michael@0 256
michael@0 257 peer->SendPacket(static_cast<unsigned char *>(buffer), length);
michael@0 258
michael@0 259 return 0;
michael@0 260 }
michael@0 261
michael@0 262 static int receive_cb(struct socket* sock, union sctp_sockstore addr,
michael@0 263 void *data, size_t datalen,
michael@0 264 struct sctp_rcvinfo rcv, int flags, void *ulp_info) {
michael@0 265 TransportTestPeer *me = static_cast<TransportTestPeer *>(
michael@0 266 addr.sconn.sconn_addr);
michael@0 267 MOZ_ASSERT(me);
michael@0 268
michael@0 269 if (flags & MSG_NOTIFICATION) {
michael@0 270 union sctp_notification *notif =
michael@0 271 static_cast<union sctp_notification *>(data);
michael@0 272
michael@0 273 me->Notification(notif, datalen);
michael@0 274 return 0;
michael@0 275 }
michael@0 276
michael@0 277 me->received_ += datalen;
michael@0 278
michael@0 279 std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
michael@0 280
michael@0 281 return 0;
michael@0 282 }
michael@0 283
michael@0 284
michael@0 285 private:
michael@0 286 std::string name_;
michael@0 287 bool connected_;
michael@0 288 size_t sent_;
michael@0 289 size_t received_;
michael@0 290 mozilla::RefPtr<TransportFlow> flow_;
michael@0 291 TransportLayerLoopback *loopback_;
michael@0 292
michael@0 293 struct sockaddr_conn local_addr_;
michael@0 294 struct sockaddr_conn remote_addr_;
michael@0 295 struct socket *sctp_;
michael@0 296 nsCOMPtr<nsITimer> timer_;
michael@0 297 nsRefPtr<SendPeriodic> periodic_;
michael@0 298 };
michael@0 299
michael@0 300
michael@0 301 // Implemented here because it calls a method of TransportTestPeer
michael@0 302 NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) {
michael@0 303 peer_->SendOne();
michael@0 304 --to_send_;
michael@0 305 if (!to_send_) {
michael@0 306 timer->Cancel();
michael@0 307 }
michael@0 308 return NS_OK;
michael@0 309 }
michael@0 310
michael@0 311 class TransportTest : public ::testing::Test {
michael@0 312 public:
michael@0 313 TransportTest() {
michael@0 314 }
michael@0 315
michael@0 316 ~TransportTest() {
michael@0 317 if (p1_)
michael@0 318 p1_->Disconnect();
michael@0 319 if (p2_)
michael@0 320 p2_->Disconnect();
michael@0 321 delete p1_;
michael@0 322 delete p2_;
michael@0 323 }
michael@0 324
michael@0 325 static void debug_printf(const char *format, ...) {
michael@0 326 va_list ap;
michael@0 327
michael@0 328 va_start(ap, format);
michael@0 329 vprintf(format, ap);
michael@0 330 va_end(ap);
michael@0 331 }
michael@0 332
michael@0 333
michael@0 334 static void SetUpTestCase() {
michael@0 335 if (sctp_logging) {
michael@0 336 usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf);
michael@0 337 usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
michael@0 338 } else {
michael@0 339 usrsctp_init(0, &TransportTestPeer::conn_output, nullptr);
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 void SetUp() {
michael@0 344 }
michael@0 345
michael@0 346 void ConnectSocket(int p1port = 0, int p2port = 0) {
michael@0 347 if (!p1port)
michael@0 348 p1port = port_number++;
michael@0 349 if (!p2port)
michael@0 350 p2port = port_number++;
michael@0 351
michael@0 352 p1_ = new TransportTestPeer("P1", p1port, p2port);
michael@0 353 p2_ = new TransportTestPeer("P2", p2port, p1port);
michael@0 354
michael@0 355 p1_->ConnectSocket(p2_);
michael@0 356 p2_->ConnectSocket(p1_);
michael@0 357 ASSERT_TRUE_WAIT(p1_->connected(), 2000);
michael@0 358 ASSERT_TRUE_WAIT(p2_->connected(), 2000);
michael@0 359 }
michael@0 360
michael@0 361 void TestTransfer(int expected = 1) {
michael@0 362 std::cerr << "Starting trasnsfer test" << std::endl;
michael@0 363 p1_->StartTransfer(expected);
michael@0 364 ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000);
michael@0 365 ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000);
michael@0 366 std::cerr << "P2 received " << p2_->received() << std::endl;
michael@0 367 }
michael@0 368
michael@0 369 protected:
michael@0 370 TransportTestPeer *p1_;
michael@0 371 TransportTestPeer *p2_;
michael@0 372 };
michael@0 373
michael@0 374 TEST_F(TransportTest, TestConnect) {
michael@0 375 ConnectSocket();
michael@0 376 }
michael@0 377
michael@0 378 TEST_F(TransportTest, TestConnectSymmetricalPorts) {
michael@0 379 ConnectSocket(5002,5002);
michael@0 380 }
michael@0 381
michael@0 382 TEST_F(TransportTest, TestTransfer) {
michael@0 383 ConnectSocket();
michael@0 384 TestTransfer(50);
michael@0 385 }
michael@0 386
michael@0 387
michael@0 388 } // end namespace
michael@0 389
michael@0 390 int main(int argc, char **argv)
michael@0 391 {
michael@0 392 test_utils = new MtransportTestUtils();
michael@0 393 // Start the tests
michael@0 394 ::testing::InitGoogleTest(&argc, argv);
michael@0 395
michael@0 396 for(int i=0; i<argc; i++) {
michael@0 397 if (!strcmp(argv[i],"-v")) {
michael@0 398 sctp_logging = true;
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 int rv = RUN_ALL_TESTS();
michael@0 403 delete test_utils;
michael@0 404 return rv;
michael@0 405 }

mercurial