Tue, 06 Jan 2015 21:39:09 +0100
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 | } |