media/mtransport/test/buffered_stun_socket_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
michael@0 11 #include "nspr.h"
michael@0 12 #include "nss.h"
michael@0 13 #include "ssl.h"
michael@0 14
michael@0 15 #include "mozilla/Scoped.h"
michael@0 16
michael@0 17 extern "C" {
michael@0 18 #include "nr_api.h"
michael@0 19 #include "nr_socket.h"
michael@0 20 #include "nr_socket_buffered_stun.h"
michael@0 21 #include "transport_addr.h"
michael@0 22 #include "stun.h"
michael@0 23 }
michael@0 24
michael@0 25 #include "databuffer.h"
michael@0 26 #include "mtransport_test_utils.h"
michael@0 27
michael@0 28 #include "nr_socket_prsock.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 using namespace mozilla;
michael@0 35 MtransportTestUtils *test_utils;
michael@0 36
michael@0 37 static uint8_t kStunMessage[] = {
michael@0 38 0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
michael@0 39 0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
michael@0 40 0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
michael@0 41 0xdb, 0x35, 0x5f, 0xaa
michael@0 42 };
michael@0 43 static size_t kStunMessageLen = sizeof(kStunMessage);
michael@0 44
michael@0 45 class DummySocket : public NrSocketBase {
michael@0 46 public:
michael@0 47 DummySocket()
michael@0 48 : writable_(UINT_MAX),
michael@0 49 write_buffer_(nullptr),
michael@0 50 readable_(UINT_MAX),
michael@0 51 read_buffer_(nullptr),
michael@0 52 cb_(nullptr),
michael@0 53 cb_arg_(nullptr),
michael@0 54 self_(nullptr) {}
michael@0 55
michael@0 56 // the nr_socket APIs
michael@0 57 virtual int create(nr_transport_addr *addr) {
michael@0 58 return 0;
michael@0 59 }
michael@0 60
michael@0 61 virtual int sendto(const void *msg, size_t len,
michael@0 62 int flags, nr_transport_addr *to) {
michael@0 63 MOZ_CRASH();
michael@0 64 return 0;
michael@0 65 }
michael@0 66
michael@0 67 virtual int recvfrom(void * buf, size_t maxlen,
michael@0 68 size_t *len, int flags,
michael@0 69 nr_transport_addr *from) {
michael@0 70 MOZ_CRASH();
michael@0 71 return 0;
michael@0 72 }
michael@0 73
michael@0 74 virtual int getaddr(nr_transport_addr *addrp) {
michael@0 75 MOZ_CRASH();
michael@0 76 return 0;
michael@0 77 }
michael@0 78
michael@0 79 virtual void close() {
michael@0 80 }
michael@0 81
michael@0 82 virtual int connect(nr_transport_addr *addr) {
michael@0 83 return 0;
michael@0 84 }
michael@0 85
michael@0 86 virtual int write(const void *msg, size_t len, size_t *written) {
michael@0 87 // Shouldn't be anything here.
michael@0 88 EXPECT_EQ(nullptr, write_buffer_.get());
michael@0 89
michael@0 90 size_t to_write = std::min(len, writable_);
michael@0 91
michael@0 92 if (to_write) {
michael@0 93 write_buffer_ = new DataBuffer(
michael@0 94 static_cast<const uint8_t *>(msg), to_write);
michael@0 95 *written = to_write;
michael@0 96 }
michael@0 97
michael@0 98 return 0;
michael@0 99 }
michael@0 100
michael@0 101 virtual int read(void* buf, size_t maxlen, size_t *len) {
michael@0 102 if (!read_buffer_.get()) {
michael@0 103 return R_WOULDBLOCK;
michael@0 104 }
michael@0 105
michael@0 106 size_t to_read = std::min(read_buffer_->len(),
michael@0 107 std::min(maxlen, readable_));
michael@0 108
michael@0 109 memcpy(buf, read_buffer_->data(), to_read);
michael@0 110 *len = to_read;
michael@0 111
michael@0 112 if (to_read < read_buffer_->len()) {
michael@0 113 read_buffer_ = new DataBuffer(read_buffer_->data() + to_read,
michael@0 114 read_buffer_->len() - to_read);
michael@0 115 } else {
michael@0 116 read_buffer_ = nullptr;
michael@0 117 }
michael@0 118
michael@0 119 return 0;
michael@0 120 }
michael@0 121
michael@0 122 // Implementations of the async_event APIs.
michael@0 123 // These are no-ops because we handle scheduling manually
michael@0 124 // for test purposes.
michael@0 125 virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
michael@0 126 char *function, int line) {
michael@0 127 EXPECT_EQ(nullptr, cb_);
michael@0 128 cb_ = cb;
michael@0 129 cb_arg_ = cb_arg;
michael@0 130
michael@0 131 return 0;
michael@0 132 }
michael@0 133
michael@0 134 virtual int cancel(int how) {
michael@0 135 cb_ = nullptr;
michael@0 136 cb_arg_ = nullptr;
michael@0 137
michael@0 138 return 0;
michael@0 139 }
michael@0 140
michael@0 141
michael@0 142 // Read/Manipulate the current state.
michael@0 143 void CheckWriteBuffer(uint8_t *data, size_t len) {
michael@0 144 if (!len) {
michael@0 145 EXPECT_EQ(nullptr, write_buffer_.get());
michael@0 146 } else {
michael@0 147 EXPECT_NE(nullptr, write_buffer_.get());
michael@0 148 ASSERT_EQ(len, write_buffer_->len());
michael@0 149 ASSERT_EQ(0, memcmp(data, write_buffer_->data(), len));
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 void ClearWriteBuffer() {
michael@0 154 write_buffer_ = nullptr;
michael@0 155 }
michael@0 156
michael@0 157 void SetWritable(size_t val) {
michael@0 158 writable_ = val;
michael@0 159 }
michael@0 160
michael@0 161 void FireWritableCb() {
michael@0 162 NR_async_cb cb = cb_;
michael@0 163 void *cb_arg = cb_arg_;
michael@0 164
michael@0 165 cb_ = nullptr;
michael@0 166 cb_arg_ = nullptr;
michael@0 167
michael@0 168 cb(this, NR_ASYNC_WAIT_WRITE, cb_arg);
michael@0 169 }
michael@0 170
michael@0 171 void SetReadBuffer(uint8_t *data, size_t len) {
michael@0 172 EXPECT_EQ(nullptr, write_buffer_.get());
michael@0 173 read_buffer_ = new DataBuffer(data, len);
michael@0 174 }
michael@0 175
michael@0 176 void ClearReadBuffer() {
michael@0 177 read_buffer_ = nullptr;
michael@0 178 }
michael@0 179
michael@0 180 void SetReadable(size_t val) {
michael@0 181 readable_ = val;
michael@0 182 }
michael@0 183
michael@0 184 nr_socket *get_nr_socket() {
michael@0 185 if (!self_) {
michael@0 186 int r = nr_socket_create_int(this, vtbl(), &self_);
michael@0 187 AddRef();
michael@0 188 if (r)
michael@0 189 return nullptr;
michael@0 190 }
michael@0 191
michael@0 192 return self_;
michael@0 193 }
michael@0 194
michael@0 195 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
michael@0 196
michael@0 197 private:
michael@0 198 DISALLOW_COPY_ASSIGN(DummySocket);
michael@0 199
michael@0 200 size_t writable_; // Amount we allow someone to write.
michael@0 201 ScopedDeletePtr<DataBuffer> write_buffer_;
michael@0 202 size_t readable_; // Amount we allow someone to read.
michael@0 203 ScopedDeletePtr<DataBuffer> read_buffer_;
michael@0 204
michael@0 205 NR_async_cb cb_;
michael@0 206 void *cb_arg_;
michael@0 207 nr_socket *self_;
michael@0 208 };
michael@0 209
michael@0 210 class BufferedStunSocketTest : public ::testing::Test {
michael@0 211 public:
michael@0 212 BufferedStunSocketTest()
michael@0 213 : dummy_(nullptr),
michael@0 214 test_socket_(nullptr) {}
michael@0 215
michael@0 216 ~BufferedStunSocketTest() {
michael@0 217 nr_socket_destroy(&test_socket_);
michael@0 218 }
michael@0 219
michael@0 220 void SetUp() {
michael@0 221 ScopedDeletePtr<DummySocket> dummy(new DummySocket());
michael@0 222
michael@0 223 int r = nr_socket_buffered_stun_create(
michael@0 224 dummy->get_nr_socket(),
michael@0 225 kStunMessageLen,
michael@0 226 &test_socket_);
michael@0 227 ASSERT_EQ(0, r);
michael@0 228 dummy_ = dummy.forget(); // Now owned by test_socket_.
michael@0 229
michael@0 230 r = nr_ip4_str_port_to_transport_addr(
michael@0 231 (char *)"192.0.2.133", 3333, IPPROTO_TCP, &remote_addr_);
michael@0 232 ASSERT_EQ(0, r);
michael@0 233
michael@0 234 r = nr_socket_connect(test_socket_,
michael@0 235 &remote_addr_);
michael@0 236 ASSERT_EQ(0, r);
michael@0 237 }
michael@0 238
michael@0 239 nr_socket *socket() { return test_socket_; }
michael@0 240
michael@0 241 protected:
michael@0 242 DummySocket *dummy_;
michael@0 243 nr_socket *test_socket_;
michael@0 244 nr_transport_addr remote_addr_;
michael@0 245 };
michael@0 246
michael@0 247
michael@0 248 TEST_F(BufferedStunSocketTest, TestCreate) {
michael@0 249 }
michael@0 250
michael@0 251 TEST_F(BufferedStunSocketTest, TestSendTo) {
michael@0 252 int r = nr_socket_sendto(test_socket_,
michael@0 253 kStunMessage,
michael@0 254 kStunMessageLen,
michael@0 255 0, &remote_addr_);
michael@0 256 ASSERT_EQ(0, r);
michael@0 257
michael@0 258 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
michael@0 259 }
michael@0 260
michael@0 261 TEST_F(BufferedStunSocketTest, TestSendToBuffered) {
michael@0 262 dummy_->SetWritable(0);
michael@0 263
michael@0 264 int r = nr_socket_sendto(test_socket_,
michael@0 265 kStunMessage,
michael@0 266 kStunMessageLen,
michael@0 267 0, &remote_addr_);
michael@0 268 ASSERT_EQ(0, r);
michael@0 269
michael@0 270 dummy_->CheckWriteBuffer(nullptr, 0);
michael@0 271
michael@0 272 dummy_->SetWritable(kStunMessageLen);
michael@0 273 dummy_->FireWritableCb();
michael@0 274 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
michael@0 275 }
michael@0 276
michael@0 277 TEST_F(BufferedStunSocketTest, TestSendFullThenDrain) {
michael@0 278 dummy_->SetWritable(0);
michael@0 279
michael@0 280 for (;;) {
michael@0 281 int r = nr_socket_sendto(test_socket_,
michael@0 282 kStunMessage,
michael@0 283 kStunMessageLen,
michael@0 284 0, &remote_addr_);
michael@0 285 if (r == R_WOULDBLOCK)
michael@0 286 break;
michael@0 287
michael@0 288 ASSERT_EQ(0, r);
michael@0 289 }
michael@0 290
michael@0 291 // Nothing was written.
michael@0 292 dummy_->CheckWriteBuffer(nullptr, 0);
michael@0 293
michael@0 294 // Now flush.
michael@0 295 dummy_->SetWritable(kStunMessageLen);
michael@0 296 dummy_->FireWritableCb();
michael@0 297 dummy_->ClearWriteBuffer();
michael@0 298
michael@0 299 // Verify we can write something.
michael@0 300 int r = nr_socket_sendto(test_socket_,
michael@0 301 kStunMessage,
michael@0 302 kStunMessageLen,
michael@0 303 0, &remote_addr_);
michael@0 304 ASSERT_EQ(0, r);
michael@0 305
michael@0 306 // And that it appears.
michael@0 307 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
michael@0 308 }
michael@0 309
michael@0 310 TEST_F(BufferedStunSocketTest, TestSendToPartialBuffered) {
michael@0 311 dummy_->SetWritable(10);
michael@0 312
michael@0 313 int r = nr_socket_sendto(test_socket_,
michael@0 314 kStunMessage,
michael@0 315 kStunMessageLen,
michael@0 316 0, &remote_addr_);
michael@0 317 ASSERT_EQ(0, r);
michael@0 318
michael@0 319 dummy_->CheckWriteBuffer(kStunMessage, 10);
michael@0 320 dummy_->ClearWriteBuffer();
michael@0 321
michael@0 322 dummy_->SetWritable(kStunMessageLen);
michael@0 323 dummy_->FireWritableCb();
michael@0 324 dummy_->CheckWriteBuffer(kStunMessage + 10, kStunMessageLen - 10);
michael@0 325 }
michael@0 326
michael@0 327 TEST_F(BufferedStunSocketTest, TestSendToReject) {
michael@0 328 dummy_->SetWritable(0);
michael@0 329
michael@0 330 int r = nr_socket_sendto(test_socket_,
michael@0 331 kStunMessage,
michael@0 332 kStunMessageLen,
michael@0 333 0, &remote_addr_);
michael@0 334 ASSERT_EQ(0, r);
michael@0 335
michael@0 336 dummy_->CheckWriteBuffer(nullptr, 0);
michael@0 337
michael@0 338 r = nr_socket_sendto(test_socket_,
michael@0 339 kStunMessage,
michael@0 340 kStunMessageLen,
michael@0 341 0, &remote_addr_);
michael@0 342 ASSERT_EQ(R_WOULDBLOCK, r);
michael@0 343
michael@0 344 dummy_->CheckWriteBuffer(nullptr, 0);
michael@0 345 }
michael@0 346
michael@0 347 TEST_F(BufferedStunSocketTest, TestSendToWrongAddr) {
michael@0 348 nr_transport_addr addr;
michael@0 349
michael@0 350 int r = nr_ip4_str_port_to_transport_addr(
michael@0 351 (char *)"192.0.2.134", 3333, IPPROTO_TCP, &addr);
michael@0 352 ASSERT_EQ(0, r);
michael@0 353
michael@0 354 r = nr_socket_sendto(test_socket_,
michael@0 355 kStunMessage,
michael@0 356 kStunMessageLen,
michael@0 357 0, &addr);
michael@0 358 ASSERT_EQ(R_BAD_DATA, r);
michael@0 359 }
michael@0 360
michael@0 361 TEST_F(BufferedStunSocketTest, TestReceiveRecvFrom) {
michael@0 362 dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
michael@0 363
michael@0 364 unsigned char tmp[2048];
michael@0 365 size_t len;
michael@0 366 nr_transport_addr addr;
michael@0 367
michael@0 368 int r = nr_socket_recvfrom(test_socket_,
michael@0 369 tmp, sizeof(tmp), &len, 0,
michael@0 370 &addr);
michael@0 371 ASSERT_EQ(0, r);
michael@0 372 ASSERT_EQ(kStunMessageLen, len);
michael@0 373 ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
michael@0 374 ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
michael@0 375 NR_TRANSPORT_ADDR_CMP_MODE_ALL));
michael@0 376 }
michael@0 377
michael@0 378 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromPartial) {
michael@0 379 dummy_->SetReadBuffer(kStunMessage, 15);
michael@0 380
michael@0 381 unsigned char tmp[2048];
michael@0 382 size_t len;
michael@0 383 nr_transport_addr addr;
michael@0 384
michael@0 385 int r = nr_socket_recvfrom(test_socket_,
michael@0 386 tmp, sizeof(tmp), &len, 0,
michael@0 387 &addr);
michael@0 388 ASSERT_EQ(R_WOULDBLOCK, r);
michael@0 389
michael@0 390
michael@0 391 dummy_->SetReadBuffer(kStunMessage + 15, kStunMessageLen - 15);
michael@0 392
michael@0 393 r = nr_socket_recvfrom(test_socket_,
michael@0 394 tmp, sizeof(tmp), &len, 0,
michael@0 395 &addr);
michael@0 396 ASSERT_EQ(0, r);
michael@0 397 ASSERT_EQ(kStunMessageLen, len);
michael@0 398 ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
michael@0 399 ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
michael@0 400 NR_TRANSPORT_ADDR_CMP_MODE_ALL));
michael@0 401
michael@0 402 r = nr_socket_recvfrom(test_socket_,
michael@0 403 tmp, sizeof(tmp), &len, 0,
michael@0 404 &addr);
michael@0 405 ASSERT_EQ(R_WOULDBLOCK, r);
michael@0 406 }
michael@0 407
michael@0 408
michael@0 409 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromGarbage) {
michael@0 410 uint8_t garbage[50];
michael@0 411 memset(garbage, 0xff, sizeof(garbage));
michael@0 412
michael@0 413 dummy_->SetReadBuffer(garbage, sizeof(garbage));
michael@0 414
michael@0 415 unsigned char tmp[2048];
michael@0 416 size_t len;
michael@0 417 nr_transport_addr addr;
michael@0 418 int r = nr_socket_recvfrom(test_socket_,
michael@0 419 tmp, sizeof(tmp), &len, 0,
michael@0 420 &addr);
michael@0 421 ASSERT_EQ(R_BAD_DATA, r);
michael@0 422
michael@0 423 r = nr_socket_recvfrom(test_socket_,
michael@0 424 tmp, sizeof(tmp), &len, 0,
michael@0 425 &addr);
michael@0 426 ASSERT_EQ(R_FAILED, r);
michael@0 427 }
michael@0 428
michael@0 429 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromTooShort) {
michael@0 430 dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
michael@0 431
michael@0 432 unsigned char tmp[2048];
michael@0 433 size_t len;
michael@0 434 nr_transport_addr addr;
michael@0 435
michael@0 436 int r = nr_socket_recvfrom(test_socket_,
michael@0 437 tmp, kStunMessageLen - 1, &len, 0,
michael@0 438 &addr);
michael@0 439 ASSERT_EQ(R_BAD_ARGS, r);
michael@0 440 }
michael@0 441
michael@0 442 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromReallyLong) {
michael@0 443 uint8_t garbage[4096];
michael@0 444 memset(garbage, 0xff, sizeof(garbage));
michael@0 445 memcpy(garbage, kStunMessage, kStunMessageLen);
michael@0 446 nr_stun_message_header *hdr = reinterpret_cast<nr_stun_message_header *>
michael@0 447 (garbage);
michael@0 448 hdr->length = htons(3000);
michael@0 449
michael@0 450 dummy_->SetReadBuffer(garbage, sizeof(garbage));
michael@0 451
michael@0 452 unsigned char tmp[4096];
michael@0 453 size_t len;
michael@0 454 nr_transport_addr addr;
michael@0 455
michael@0 456 int r = nr_socket_recvfrom(test_socket_,
michael@0 457 tmp, kStunMessageLen - 1, &len, 0,
michael@0 458 &addr);
michael@0 459 ASSERT_EQ(R_BAD_DATA, r);
michael@0 460 }
michael@0 461
michael@0 462
michael@0 463
michael@0 464 int main(int argc, char **argv)
michael@0 465 {
michael@0 466 test_utils = new MtransportTestUtils();
michael@0 467 NSS_NoDB_Init(nullptr);
michael@0 468 NSS_SetDomesticPolicy();
michael@0 469
michael@0 470 // Start the tests
michael@0 471 ::testing::InitGoogleTest(&argc, argv);
michael@0 472
michael@0 473 int rv = RUN_ALL_TESTS();
michael@0 474
michael@0 475 delete test_utils;
michael@0 476 return rv;
michael@0 477 }

mercurial