Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |