media/mtransport/test/turn_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 // Some code copied from nICEr. License is:
michael@0 10 /*
michael@0 11 Copyright (c) 2007, Adobe Systems, Incorporated
michael@0 12 All rights reserved.
michael@0 13
michael@0 14 Redistribution and use in source and binary forms, with or without
michael@0 15 modification, are permitted provided that the following conditions are
michael@0 16 met:
michael@0 17
michael@0 18 * Redistributions of source code must retain the above copyright
michael@0 19 notice, this list of conditions and the following disclaimer.
michael@0 20
michael@0 21 * Redistributions in binary form must reproduce the above copyright
michael@0 22 notice, this list of conditions and the following disclaimer in the
michael@0 23 documentation and/or other materials provided with the distribution.
michael@0 24
michael@0 25 * Neither the name of Adobe Systems, Network Resonance nor the names of its
michael@0 26 contributors may be used to endorse or promote products derived from
michael@0 27 this software without specific prior written permission.
michael@0 28
michael@0 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 40 */
michael@0 41
michael@0 42 #include <stdlib.h>
michael@0 43 #include <iostream>
michael@0 44
michael@0 45 #include "sigslot.h"
michael@0 46
michael@0 47 #include "logging.h"
michael@0 48 #include "nspr.h"
michael@0 49 #include "nss.h"
michael@0 50 #include "ssl.h"
michael@0 51
michael@0 52 #include "mozilla/Scoped.h"
michael@0 53 #include "nsThreadUtils.h"
michael@0 54 #include "nsXPCOM.h"
michael@0 55
michael@0 56 #include "mtransport_test_utils.h"
michael@0 57 #include "runnable_utils.h"
michael@0 58
michael@0 59 #define GTEST_HAS_RTTI 0
michael@0 60 #include "gtest/gtest.h"
michael@0 61 #include "gtest_utils.h"
michael@0 62
michael@0 63 // nICEr includes
michael@0 64 extern "C" {
michael@0 65 #include "nr_api.h"
michael@0 66 #include "registry.h"
michael@0 67 #include "async_timer.h"
michael@0 68 #include "r_crc32.h"
michael@0 69 #include "ice_util.h"
michael@0 70 #include "transport_addr.h"
michael@0 71 #include "nr_crypto.h"
michael@0 72 #include "nr_socket.h"
michael@0 73 #include "nr_socket_local.h"
michael@0 74 #include "nr_socket_buffered_stun.h"
michael@0 75 #include "stun_client_ctx.h"
michael@0 76 #include "turn_client_ctx.h"
michael@0 77 }
michael@0 78
michael@0 79 #include "nricemediastream.h"
michael@0 80 #include "nricectx.h"
michael@0 81
michael@0 82
michael@0 83 MtransportTestUtils *test_utils;
michael@0 84
michael@0 85 using namespace mozilla;
michael@0 86
michael@0 87 std::string g_turn_server;
michael@0 88 std::string g_turn_user;
michael@0 89 std::string g_turn_password;
michael@0 90
michael@0 91 std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737
michael@0 92
michael@0 93 class TurnClient : public ::testing::Test {
michael@0 94 public:
michael@0 95 TurnClient()
michael@0 96 : turn_server_(g_turn_server),
michael@0 97 real_socket_(nullptr),
michael@0 98 net_socket_(nullptr),
michael@0 99 buffered_socket_(nullptr),
michael@0 100 net_fd_(nullptr),
michael@0 101 turn_ctx_(nullptr),
michael@0 102 allocated_(false),
michael@0 103 received_(0),
michael@0 104 protocol_(IPPROTO_UDP) {
michael@0 105 }
michael@0 106
michael@0 107 ~TurnClient() {
michael@0 108 }
michael@0 109
michael@0 110 void SetTcp() {
michael@0 111 protocol_ = IPPROTO_TCP;
michael@0 112 }
michael@0 113
michael@0 114 void Init_s() {
michael@0 115 int r;
michael@0 116 nr_transport_addr addr;
michael@0 117 r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
michael@0 118 ASSERT_EQ(0, r);
michael@0 119
michael@0 120 r = nr_socket_local_create(&addr, &real_socket_);
michael@0 121 ASSERT_EQ(0, r);
michael@0 122
michael@0 123 if (protocol_ == IPPROTO_TCP) {
michael@0 124 int r =
michael@0 125 nr_socket_buffered_stun_create(real_socket_, 100000,
michael@0 126 &buffered_socket_);
michael@0 127 ASSERT_EQ(0, r);
michael@0 128 net_socket_ = buffered_socket_;
michael@0 129 } else {
michael@0 130 net_socket_ = real_socket_;
michael@0 131 }
michael@0 132
michael@0 133 r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478,
michael@0 134 protocol_, &addr);
michael@0 135 ASSERT_EQ(0, r);
michael@0 136
michael@0 137 std::vector<unsigned char> password_vec(
michael@0 138 g_turn_password.begin(), g_turn_password.end());
michael@0 139 Data password;
michael@0 140 INIT_DATA(password, &password_vec[0], password_vec.size());
michael@0 141 r = nr_turn_client_ctx_create("test", net_socket_,
michael@0 142 g_turn_user.c_str(),
michael@0 143 &password,
michael@0 144 &addr, &turn_ctx_);
michael@0 145 ASSERT_EQ(0, r);
michael@0 146
michael@0 147 r = nr_socket_getfd(net_socket_, &net_fd_);
michael@0 148 ASSERT_EQ(0, r);
michael@0 149
michael@0 150 NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
michael@0 151 (void *)this);
michael@0 152 }
michael@0 153
michael@0 154 void TearDown_s() {
michael@0 155 nr_turn_client_ctx_destroy(&turn_ctx_);
michael@0 156 if (net_fd_) {
michael@0 157 NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
michael@0 158 }
michael@0 159
michael@0 160 nr_socket_destroy(&buffered_socket_);
michael@0 161 }
michael@0 162
michael@0 163 void TearDown() {
michael@0 164 RUN_ON_THREAD(test_utils->sts_target(),
michael@0 165 WrapRunnable(this, &TurnClient::TearDown_s),
michael@0 166 NS_DISPATCH_SYNC);
michael@0 167 }
michael@0 168
michael@0 169 void Allocate_s() {
michael@0 170 Init_s();
michael@0 171 ASSERT_TRUE(turn_ctx_);
michael@0 172
michael@0 173 int r = nr_turn_client_allocate(turn_ctx_,
michael@0 174 allocate_success_cb,
michael@0 175 this);
michael@0 176 ASSERT_EQ(0, r);
michael@0 177 }
michael@0 178
michael@0 179 void Allocate(bool expect_success=true) {
michael@0 180 RUN_ON_THREAD(test_utils->sts_target(),
michael@0 181 WrapRunnable(this, &TurnClient::Allocate_s),
michael@0 182 NS_DISPATCH_SYNC);
michael@0 183
michael@0 184 if (expect_success) {
michael@0 185 ASSERT_TRUE_WAIT(allocated_, 5000);
michael@0 186 }
michael@0 187 else {
michael@0 188 PR_Sleep(10000);
michael@0 189 ASSERT_FALSE(allocated_);
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 void Allocated() {
michael@0 194 if (turn_ctx_->state!=NR_TURN_CLIENT_STATE_ALLOCATED) {
michael@0 195 std::cerr << "Allocation failed" << std::endl;
michael@0 196 return;
michael@0 197 }
michael@0 198 allocated_ = true;
michael@0 199
michael@0 200 int r;
michael@0 201 nr_transport_addr addr;
michael@0 202
michael@0 203 r = nr_turn_client_get_relayed_address(turn_ctx_, &addr);
michael@0 204 ASSERT_EQ(0, r);
michael@0 205
michael@0 206 relay_addr_ = addr.as_string;
michael@0 207
michael@0 208 std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl;
michael@0 209 }
michael@0 210
michael@0 211 void Readable(NR_SOCKET s, int how, void *arg) {
michael@0 212 // Re-arm
michael@0 213 std::cerr << "Socket is readable" << std::endl;
michael@0 214 NR_ASYNC_WAIT(s, how, socket_readable_cb, arg);
michael@0 215
michael@0 216 UCHAR buf[8192];
michael@0 217 size_t len_s;
michael@0 218 nr_transport_addr addr;
michael@0 219
michael@0 220 int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr);
michael@0 221 if (r) {
michael@0 222 std::cerr << "Error reading from socket" << std::endl;
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 ASSERT_LT(len_s, (size_t)INT_MAX);
michael@0 227 int len = (int)len_s;
michael@0 228
michael@0 229 if (nr_is_stun_response_message(buf, len)) {
michael@0 230 std::cerr << "STUN response" << std::endl;
michael@0 231 r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr);
michael@0 232
michael@0 233 if (r && r != R_REJECTED && r != R_RETRY) {
michael@0 234 std::cerr << "Error processing STUN: " << r << std::endl;
michael@0 235 }
michael@0 236 } else if (nr_is_stun_indication_message(buf, len)) {
michael@0 237 std::cerr << "STUN indication" << std::endl;
michael@0 238
michael@0 239 /* Process the indication */
michael@0 240 unsigned char data[NR_STUN_MAX_MESSAGE_SIZE];
michael@0 241 size_t datal;
michael@0 242 nr_transport_addr remote_addr;
michael@0 243
michael@0 244 r = nr_turn_client_parse_data_indication(turn_ctx_, &addr,
michael@0 245 buf, len,
michael@0 246 data, &datal, sizeof(data),
michael@0 247 &remote_addr);
michael@0 248 ASSERT_EQ(0, r);
michael@0 249 std::cerr << "Received " << datal << " bytes from "
michael@0 250 << remote_addr.as_string << std::endl;
michael@0 251
michael@0 252 received_ += datal;
michael@0 253
michael@0 254 for (size_t i=0; i < datal; i++) {
michael@0 255 ASSERT_EQ(i & 0xff, data[i]);
michael@0 256 }
michael@0 257 }
michael@0 258 else {
michael@0 259 if (nr_is_stun_message(buf, len)) {
michael@0 260 std::cerr << "STUN message of unexpected type" << std::endl;
michael@0 261 } else {
michael@0 262 std::cerr << "Not a STUN message" << std::endl;
michael@0 263 }
michael@0 264 return;
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 void SendTo_s(const std::string& target) {
michael@0 269 nr_transport_addr addr;
michael@0 270 int r;
michael@0 271
michael@0 272 // Expected pattern here is "IP4:127.0.0.1:3487"
michael@0 273 ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
michael@0 274
michael@0 275 size_t offset = target.rfind(':');
michael@0 276 ASSERT_NE(offset, std::string::npos);
michael@0 277
michael@0 278 std::string host = target.substr(4, offset - 4);
michael@0 279 std::string port = target.substr(offset + 1);
michael@0 280
michael@0 281 r = nr_ip4_str_port_to_transport_addr(host.c_str(),
michael@0 282 atoi(port.c_str()),
michael@0 283 IPPROTO_UDP,
michael@0 284 &addr);
michael@0 285 ASSERT_EQ(0, r);
michael@0 286
michael@0 287 unsigned char test[100];
michael@0 288 for (size_t i=0; i<sizeof(test); i++) {
michael@0 289 test[i] = i & 0xff;
michael@0 290 }
michael@0 291
michael@0 292 r = nr_turn_client_send_indication(turn_ctx_,
michael@0 293 test, sizeof(test), 0,
michael@0 294 &addr);
michael@0 295 ASSERT_EQ(0, r);
michael@0 296 }
michael@0 297
michael@0 298 void SendTo(const std::string& target) {
michael@0 299 RUN_ON_THREAD(test_utils->sts_target(),
michael@0 300 WrapRunnable(this, &TurnClient::SendTo_s, target),
michael@0 301 NS_DISPATCH_SYNC);
michael@0 302 }
michael@0 303
michael@0 304 int received() const { return received_; }
michael@0 305
michael@0 306 static void socket_readable_cb(NR_SOCKET s, int how, void *arg) {
michael@0 307 static_cast<TurnClient *>(arg)->Readable(s, how, arg);
michael@0 308 }
michael@0 309
michael@0 310 static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
michael@0 311 static_cast<TurnClient *>(arg)->Allocated();
michael@0 312 }
michael@0 313
michael@0 314 protected:
michael@0 315 std::string turn_server_;
michael@0 316 nr_socket *real_socket_;
michael@0 317 nr_socket *net_socket_;
michael@0 318 nr_socket *buffered_socket_;
michael@0 319 NR_SOCKET net_fd_;
michael@0 320 nr_turn_client_ctx *turn_ctx_;
michael@0 321 std::string relay_addr_;
michael@0 322 bool allocated_;
michael@0 323 int received_;
michael@0 324 int protocol_;
michael@0 325 };
michael@0 326
michael@0 327 TEST_F(TurnClient, Allocate) {
michael@0 328 Allocate();
michael@0 329 }
michael@0 330
michael@0 331 TEST_F(TurnClient, AllocateTcp) {
michael@0 332 SetTcp();
michael@0 333 Allocate();
michael@0 334 }
michael@0 335
michael@0 336 TEST_F(TurnClient, AllocateAndHold) {
michael@0 337 Allocate();
michael@0 338 PR_Sleep(20000);
michael@0 339 }
michael@0 340
michael@0 341 TEST_F(TurnClient, SendToSelf) {
michael@0 342 Allocate();
michael@0 343 SendTo(relay_addr_);
michael@0 344 ASSERT_TRUE_WAIT(received() == 100, 1000);
michael@0 345 PR_Sleep(10000); // Wait 10 seconds to make sure the
michael@0 346 // CreatePermission has time to complete/fail.
michael@0 347 SendTo(relay_addr_);
michael@0 348 ASSERT_TRUE_WAIT(received() == 200, 1000);
michael@0 349 }
michael@0 350
michael@0 351
michael@0 352 TEST_F(TurnClient, SendToSelfTcp) {
michael@0 353 SetTcp();
michael@0 354 Allocate();
michael@0 355 SendTo(relay_addr_);
michael@0 356 ASSERT_TRUE_WAIT(received() == 100, 1000);
michael@0 357 PR_Sleep(10000); // Wait 10 seconds to make sure the
michael@0 358 // CreatePermission has time to complete/fail.
michael@0 359 SendTo(relay_addr_);
michael@0 360 ASSERT_TRUE_WAIT(received() == 200, 1000);
michael@0 361 }
michael@0 362
michael@0 363 TEST_F(TurnClient, AllocateDummyServer) {
michael@0 364 turn_server_ = kDummyTurnServer;
michael@0 365 Allocate(false);
michael@0 366 }
michael@0 367
michael@0 368 static std::string get_environment(const char *name) {
michael@0 369 char *value = getenv(name);
michael@0 370
michael@0 371 if (!value)
michael@0 372 return "";
michael@0 373
michael@0 374 return value;
michael@0 375 }
michael@0 376
michael@0 377 int main(int argc, char **argv)
michael@0 378 {
michael@0 379 g_turn_server = get_environment("TURN_SERVER_ADDRESS");
michael@0 380 g_turn_user = get_environment("TURN_SERVER_USER");
michael@0 381 g_turn_password = get_environment("TURN_SERVER_PASSWORD");
michael@0 382
michael@0 383 if (g_turn_server.empty() ||
michael@0 384 g_turn_user.empty(),
michael@0 385 g_turn_password.empty()) {
michael@0 386 printf(
michael@0 387 "Set TURN_SERVER_ADDRESS, TURN_SERVER_USER, and TURN_SERVER_PASSWORD\n"
michael@0 388 "environment variables to run this test\n");
michael@0 389 return 0;
michael@0 390 }
michael@0 391 {
michael@0 392 nr_transport_addr addr;
michael@0 393 if (nr_ip4_str_port_to_transport_addr(g_turn_server.c_str(), 3478,
michael@0 394 IPPROTO_UDP, &addr)) {
michael@0 395 printf("Invalid TURN_SERVER_ADDRESS \"%s\". Only IP numbers supported.\n",
michael@0 396 g_turn_server.c_str());
michael@0 397 return 0;
michael@0 398 }
michael@0 399 }
michael@0 400 test_utils = new MtransportTestUtils();
michael@0 401 NSS_NoDB_Init(nullptr);
michael@0 402 NSS_SetDomesticPolicy();
michael@0 403
michael@0 404 // Set up the ICE registry, etc.
michael@0 405 // TODO(ekr@rtfm.com): Clean up
michael@0 406 std::string dummy("dummy");
michael@0 407 RUN_ON_THREAD(test_utils->sts_target(),
michael@0 408 WrapRunnableNM(&NrIceCtx::Create,
michael@0 409 dummy, false, false),
michael@0 410 NS_DISPATCH_SYNC);
michael@0 411
michael@0 412 // Start the tests
michael@0 413 ::testing::InitGoogleTest(&argc, argv);
michael@0 414
michael@0 415 int rv = RUN_ALL_TESTS();
michael@0 416 delete test_utils;
michael@0 417 return rv;
michael@0 418 }

mercurial