1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/mtransport/test/turn_unittest.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,418 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Original author: ekr@rtfm.com 1.11 + 1.12 +// Some code copied from nICEr. License is: 1.13 +/* 1.14 +Copyright (c) 2007, Adobe Systems, Incorporated 1.15 +All rights reserved. 1.16 + 1.17 +Redistribution and use in source and binary forms, with or without 1.18 +modification, are permitted provided that the following conditions are 1.19 +met: 1.20 + 1.21 +* Redistributions of source code must retain the above copyright 1.22 + notice, this list of conditions and the following disclaimer. 1.23 + 1.24 +* Redistributions in binary form must reproduce the above copyright 1.25 + notice, this list of conditions and the following disclaimer in the 1.26 + documentation and/or other materials provided with the distribution. 1.27 + 1.28 +* Neither the name of Adobe Systems, Network Resonance nor the names of its 1.29 + contributors may be used to endorse or promote products derived from 1.30 + this software without specific prior written permission. 1.31 + 1.32 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.33 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.34 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.35 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.36 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.37 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.38 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.39 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.40 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.41 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.42 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.43 +*/ 1.44 + 1.45 +#include <stdlib.h> 1.46 +#include <iostream> 1.47 + 1.48 +#include "sigslot.h" 1.49 + 1.50 +#include "logging.h" 1.51 +#include "nspr.h" 1.52 +#include "nss.h" 1.53 +#include "ssl.h" 1.54 + 1.55 +#include "mozilla/Scoped.h" 1.56 +#include "nsThreadUtils.h" 1.57 +#include "nsXPCOM.h" 1.58 + 1.59 +#include "mtransport_test_utils.h" 1.60 +#include "runnable_utils.h" 1.61 + 1.62 +#define GTEST_HAS_RTTI 0 1.63 +#include "gtest/gtest.h" 1.64 +#include "gtest_utils.h" 1.65 + 1.66 +// nICEr includes 1.67 +extern "C" { 1.68 +#include "nr_api.h" 1.69 +#include "registry.h" 1.70 +#include "async_timer.h" 1.71 +#include "r_crc32.h" 1.72 +#include "ice_util.h" 1.73 +#include "transport_addr.h" 1.74 +#include "nr_crypto.h" 1.75 +#include "nr_socket.h" 1.76 +#include "nr_socket_local.h" 1.77 +#include "nr_socket_buffered_stun.h" 1.78 +#include "stun_client_ctx.h" 1.79 +#include "turn_client_ctx.h" 1.80 +} 1.81 + 1.82 +#include "nricemediastream.h" 1.83 +#include "nricectx.h" 1.84 + 1.85 + 1.86 +MtransportTestUtils *test_utils; 1.87 + 1.88 +using namespace mozilla; 1.89 + 1.90 +std::string g_turn_server; 1.91 +std::string g_turn_user; 1.92 +std::string g_turn_password; 1.93 + 1.94 +std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737 1.95 + 1.96 +class TurnClient : public ::testing::Test { 1.97 + public: 1.98 + TurnClient() 1.99 + : turn_server_(g_turn_server), 1.100 + real_socket_(nullptr), 1.101 + net_socket_(nullptr), 1.102 + buffered_socket_(nullptr), 1.103 + net_fd_(nullptr), 1.104 + turn_ctx_(nullptr), 1.105 + allocated_(false), 1.106 + received_(0), 1.107 + protocol_(IPPROTO_UDP) { 1.108 + } 1.109 + 1.110 + ~TurnClient() { 1.111 + } 1.112 + 1.113 + void SetTcp() { 1.114 + protocol_ = IPPROTO_TCP; 1.115 + } 1.116 + 1.117 + void Init_s() { 1.118 + int r; 1.119 + nr_transport_addr addr; 1.120 + r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr); 1.121 + ASSERT_EQ(0, r); 1.122 + 1.123 + r = nr_socket_local_create(&addr, &real_socket_); 1.124 + ASSERT_EQ(0, r); 1.125 + 1.126 + if (protocol_ == IPPROTO_TCP) { 1.127 + int r = 1.128 + nr_socket_buffered_stun_create(real_socket_, 100000, 1.129 + &buffered_socket_); 1.130 + ASSERT_EQ(0, r); 1.131 + net_socket_ = buffered_socket_; 1.132 + } else { 1.133 + net_socket_ = real_socket_; 1.134 + } 1.135 + 1.136 + r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478, 1.137 + protocol_, &addr); 1.138 + ASSERT_EQ(0, r); 1.139 + 1.140 + std::vector<unsigned char> password_vec( 1.141 + g_turn_password.begin(), g_turn_password.end()); 1.142 + Data password; 1.143 + INIT_DATA(password, &password_vec[0], password_vec.size()); 1.144 + r = nr_turn_client_ctx_create("test", net_socket_, 1.145 + g_turn_user.c_str(), 1.146 + &password, 1.147 + &addr, &turn_ctx_); 1.148 + ASSERT_EQ(0, r); 1.149 + 1.150 + r = nr_socket_getfd(net_socket_, &net_fd_); 1.151 + ASSERT_EQ(0, r); 1.152 + 1.153 + NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb, 1.154 + (void *)this); 1.155 + } 1.156 + 1.157 + void TearDown_s() { 1.158 + nr_turn_client_ctx_destroy(&turn_ctx_); 1.159 + if (net_fd_) { 1.160 + NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ); 1.161 + } 1.162 + 1.163 + nr_socket_destroy(&buffered_socket_); 1.164 + } 1.165 + 1.166 + void TearDown() { 1.167 + RUN_ON_THREAD(test_utils->sts_target(), 1.168 + WrapRunnable(this, &TurnClient::TearDown_s), 1.169 + NS_DISPATCH_SYNC); 1.170 + } 1.171 + 1.172 + void Allocate_s() { 1.173 + Init_s(); 1.174 + ASSERT_TRUE(turn_ctx_); 1.175 + 1.176 + int r = nr_turn_client_allocate(turn_ctx_, 1.177 + allocate_success_cb, 1.178 + this); 1.179 + ASSERT_EQ(0, r); 1.180 + } 1.181 + 1.182 + void Allocate(bool expect_success=true) { 1.183 + RUN_ON_THREAD(test_utils->sts_target(), 1.184 + WrapRunnable(this, &TurnClient::Allocate_s), 1.185 + NS_DISPATCH_SYNC); 1.186 + 1.187 + if (expect_success) { 1.188 + ASSERT_TRUE_WAIT(allocated_, 5000); 1.189 + } 1.190 + else { 1.191 + PR_Sleep(10000); 1.192 + ASSERT_FALSE(allocated_); 1.193 + } 1.194 + } 1.195 + 1.196 + void Allocated() { 1.197 + if (turn_ctx_->state!=NR_TURN_CLIENT_STATE_ALLOCATED) { 1.198 + std::cerr << "Allocation failed" << std::endl; 1.199 + return; 1.200 + } 1.201 + allocated_ = true; 1.202 + 1.203 + int r; 1.204 + nr_transport_addr addr; 1.205 + 1.206 + r = nr_turn_client_get_relayed_address(turn_ctx_, &addr); 1.207 + ASSERT_EQ(0, r); 1.208 + 1.209 + relay_addr_ = addr.as_string; 1.210 + 1.211 + std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl; 1.212 + } 1.213 + 1.214 + void Readable(NR_SOCKET s, int how, void *arg) { 1.215 + // Re-arm 1.216 + std::cerr << "Socket is readable" << std::endl; 1.217 + NR_ASYNC_WAIT(s, how, socket_readable_cb, arg); 1.218 + 1.219 + UCHAR buf[8192]; 1.220 + size_t len_s; 1.221 + nr_transport_addr addr; 1.222 + 1.223 + int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr); 1.224 + if (r) { 1.225 + std::cerr << "Error reading from socket" << std::endl; 1.226 + return; 1.227 + } 1.228 + 1.229 + ASSERT_LT(len_s, (size_t)INT_MAX); 1.230 + int len = (int)len_s; 1.231 + 1.232 + if (nr_is_stun_response_message(buf, len)) { 1.233 + std::cerr << "STUN response" << std::endl; 1.234 + r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr); 1.235 + 1.236 + if (r && r != R_REJECTED && r != R_RETRY) { 1.237 + std::cerr << "Error processing STUN: " << r << std::endl; 1.238 + } 1.239 + } else if (nr_is_stun_indication_message(buf, len)) { 1.240 + std::cerr << "STUN indication" << std::endl; 1.241 + 1.242 + /* Process the indication */ 1.243 + unsigned char data[NR_STUN_MAX_MESSAGE_SIZE]; 1.244 + size_t datal; 1.245 + nr_transport_addr remote_addr; 1.246 + 1.247 + r = nr_turn_client_parse_data_indication(turn_ctx_, &addr, 1.248 + buf, len, 1.249 + data, &datal, sizeof(data), 1.250 + &remote_addr); 1.251 + ASSERT_EQ(0, r); 1.252 + std::cerr << "Received " << datal << " bytes from " 1.253 + << remote_addr.as_string << std::endl; 1.254 + 1.255 + received_ += datal; 1.256 + 1.257 + for (size_t i=0; i < datal; i++) { 1.258 + ASSERT_EQ(i & 0xff, data[i]); 1.259 + } 1.260 + } 1.261 + else { 1.262 + if (nr_is_stun_message(buf, len)) { 1.263 + std::cerr << "STUN message of unexpected type" << std::endl; 1.264 + } else { 1.265 + std::cerr << "Not a STUN message" << std::endl; 1.266 + } 1.267 + return; 1.268 + } 1.269 + } 1.270 + 1.271 + void SendTo_s(const std::string& target) { 1.272 + nr_transport_addr addr; 1.273 + int r; 1.274 + 1.275 + // Expected pattern here is "IP4:127.0.0.1:3487" 1.276 + ASSERT_EQ(0, target.compare(0, 4, "IP4:")); 1.277 + 1.278 + size_t offset = target.rfind(':'); 1.279 + ASSERT_NE(offset, std::string::npos); 1.280 + 1.281 + std::string host = target.substr(4, offset - 4); 1.282 + std::string port = target.substr(offset + 1); 1.283 + 1.284 + r = nr_ip4_str_port_to_transport_addr(host.c_str(), 1.285 + atoi(port.c_str()), 1.286 + IPPROTO_UDP, 1.287 + &addr); 1.288 + ASSERT_EQ(0, r); 1.289 + 1.290 + unsigned char test[100]; 1.291 + for (size_t i=0; i<sizeof(test); i++) { 1.292 + test[i] = i & 0xff; 1.293 + } 1.294 + 1.295 + r = nr_turn_client_send_indication(turn_ctx_, 1.296 + test, sizeof(test), 0, 1.297 + &addr); 1.298 + ASSERT_EQ(0, r); 1.299 + } 1.300 + 1.301 + void SendTo(const std::string& target) { 1.302 + RUN_ON_THREAD(test_utils->sts_target(), 1.303 + WrapRunnable(this, &TurnClient::SendTo_s, target), 1.304 + NS_DISPATCH_SYNC); 1.305 + } 1.306 + 1.307 + int received() const { return received_; } 1.308 + 1.309 + static void socket_readable_cb(NR_SOCKET s, int how, void *arg) { 1.310 + static_cast<TurnClient *>(arg)->Readable(s, how, arg); 1.311 + } 1.312 + 1.313 + static void allocate_success_cb(NR_SOCKET s, int how, void *arg){ 1.314 + static_cast<TurnClient *>(arg)->Allocated(); 1.315 + } 1.316 + 1.317 + protected: 1.318 + std::string turn_server_; 1.319 + nr_socket *real_socket_; 1.320 + nr_socket *net_socket_; 1.321 + nr_socket *buffered_socket_; 1.322 + NR_SOCKET net_fd_; 1.323 + nr_turn_client_ctx *turn_ctx_; 1.324 + std::string relay_addr_; 1.325 + bool allocated_; 1.326 + int received_; 1.327 + int protocol_; 1.328 +}; 1.329 + 1.330 +TEST_F(TurnClient, Allocate) { 1.331 + Allocate(); 1.332 +} 1.333 + 1.334 +TEST_F(TurnClient, AllocateTcp) { 1.335 + SetTcp(); 1.336 + Allocate(); 1.337 +} 1.338 + 1.339 +TEST_F(TurnClient, AllocateAndHold) { 1.340 + Allocate(); 1.341 + PR_Sleep(20000); 1.342 +} 1.343 + 1.344 +TEST_F(TurnClient, SendToSelf) { 1.345 + Allocate(); 1.346 + SendTo(relay_addr_); 1.347 + ASSERT_TRUE_WAIT(received() == 100, 1000); 1.348 + PR_Sleep(10000); // Wait 10 seconds to make sure the 1.349 + // CreatePermission has time to complete/fail. 1.350 + SendTo(relay_addr_); 1.351 + ASSERT_TRUE_WAIT(received() == 200, 1000); 1.352 +} 1.353 + 1.354 + 1.355 +TEST_F(TurnClient, SendToSelfTcp) { 1.356 + SetTcp(); 1.357 + Allocate(); 1.358 + SendTo(relay_addr_); 1.359 + ASSERT_TRUE_WAIT(received() == 100, 1000); 1.360 + PR_Sleep(10000); // Wait 10 seconds to make sure the 1.361 + // CreatePermission has time to complete/fail. 1.362 + SendTo(relay_addr_); 1.363 + ASSERT_TRUE_WAIT(received() == 200, 1000); 1.364 +} 1.365 + 1.366 +TEST_F(TurnClient, AllocateDummyServer) { 1.367 + turn_server_ = kDummyTurnServer; 1.368 + Allocate(false); 1.369 +} 1.370 + 1.371 +static std::string get_environment(const char *name) { 1.372 + char *value = getenv(name); 1.373 + 1.374 + if (!value) 1.375 + return ""; 1.376 + 1.377 + return value; 1.378 +} 1.379 + 1.380 +int main(int argc, char **argv) 1.381 +{ 1.382 + g_turn_server = get_environment("TURN_SERVER_ADDRESS"); 1.383 + g_turn_user = get_environment("TURN_SERVER_USER"); 1.384 + g_turn_password = get_environment("TURN_SERVER_PASSWORD"); 1.385 + 1.386 + if (g_turn_server.empty() || 1.387 + g_turn_user.empty(), 1.388 + g_turn_password.empty()) { 1.389 + printf( 1.390 + "Set TURN_SERVER_ADDRESS, TURN_SERVER_USER, and TURN_SERVER_PASSWORD\n" 1.391 + "environment variables to run this test\n"); 1.392 + return 0; 1.393 + } 1.394 + { 1.395 + nr_transport_addr addr; 1.396 + if (nr_ip4_str_port_to_transport_addr(g_turn_server.c_str(), 3478, 1.397 + IPPROTO_UDP, &addr)) { 1.398 + printf("Invalid TURN_SERVER_ADDRESS \"%s\". Only IP numbers supported.\n", 1.399 + g_turn_server.c_str()); 1.400 + return 0; 1.401 + } 1.402 + } 1.403 + test_utils = new MtransportTestUtils(); 1.404 + NSS_NoDB_Init(nullptr); 1.405 + NSS_SetDomesticPolicy(); 1.406 + 1.407 + // Set up the ICE registry, etc. 1.408 + // TODO(ekr@rtfm.com): Clean up 1.409 + std::string dummy("dummy"); 1.410 + RUN_ON_THREAD(test_utils->sts_target(), 1.411 + WrapRunnableNM(&NrIceCtx::Create, 1.412 + dummy, false, false), 1.413 + NS_DISPATCH_SYNC); 1.414 + 1.415 + // Start the tests 1.416 + ::testing::InitGoogleTest(&argc, argv); 1.417 + 1.418 + int rv = RUN_ALL_TESTS(); 1.419 + delete test_utils; 1.420 + return rv; 1.421 +}