turn_unittest.cpp (12357B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Original author: ekr@rtfm.com 8 9 // Some code copied from nICEr. License is: 10 /* 11 Copyright (c) 2007, Adobe Systems, Incorporated 12 All rights reserved. 13 14 Redistribution and use in source and binary forms, with or without 15 modification, are permitted provided that the following conditions are 16 met: 17 18 * Redistributions of source code must retain the above copyright 19 notice, this list of conditions and the following disclaimer. 20 21 * Redistributions in binary form must reproduce the above copyright 22 notice, this list of conditions and the following disclaimer in the 23 documentation and/or other materials provided with the distribution. 24 25 * Neither the name of Adobe Systems, Network Resonance nor the names of its 26 contributors may be used to endorse or promote products derived from 27 this software without specific prior written permission. 28 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 #include <stdlib.h> 43 44 #include <iostream> 45 46 #include "runnable_utils.h" 47 48 #define GTEST_HAS_RTTI 0 49 #include "gtest/gtest.h" 50 #include "gtest_utils.h" 51 52 #define USE_TURN 53 54 // nICEr includes 55 extern "C" { 56 // clang-format off 57 #include "nr_api.h" 58 #include "transport_addr.h" 59 #include "nr_crypto.h" 60 #include "nr_socket.h" 61 #include "nr_socket_local.h" 62 #include "nr_socket_buffered_stun.h" 63 #include "stun_client_ctx.h" 64 #include "turn_client_ctx.h" 65 // clang-format on 66 } 67 68 #include "nricectx.h" 69 70 using namespace mozilla; 71 72 MOZ_RUNINIT static std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737 73 74 class TurnClient : public MtransportTest { 75 public: 76 TurnClient() 77 : real_socket_(nullptr), 78 net_socket_(nullptr), 79 buffered_socket_(nullptr), 80 net_fd_(nullptr), 81 turn_ctx_(nullptr), 82 allocated_(false), 83 received_(0), 84 protocol_(IPPROTO_UDP) {} 85 86 ~TurnClient() = default; 87 88 static void SetUpTestCase() { 89 NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); 90 } 91 92 void SetTcp() { protocol_ = IPPROTO_TCP; } 93 94 void Init_s() { 95 int r; 96 nr_transport_addr addr; 97 r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr); 98 ASSERT_EQ(0, r); 99 100 r = nr_socket_local_create(nullptr, &addr, &real_socket_); 101 ASSERT_EQ(0, r); 102 103 if (protocol_ == IPPROTO_TCP) { 104 int r = nr_socket_buffered_stun_create( 105 real_socket_, 100000, TURN_TCP_FRAMING, &buffered_socket_); 106 ASSERT_EQ(0, r); 107 net_socket_ = buffered_socket_; 108 } else { 109 net_socket_ = real_socket_; 110 } 111 112 r = nr_str_port_to_transport_addr(turn_server_.c_str(), 3478, protocol_, 113 &addr); 114 ASSERT_EQ(0, r); 115 116 std::vector<unsigned char> password_vec(turn_password_.begin(), 117 turn_password_.end()); 118 Data password; 119 INIT_DATA(password, &password_vec[0], password_vec.size()); 120 r = nr_turn_client_ctx_create("test", net_socket_, turn_user_.c_str(), 121 &password, &addr, nullptr, &turn_ctx_); 122 ASSERT_EQ(0, r); 123 124 r = nr_socket_getfd(net_socket_, &net_fd_); 125 ASSERT_EQ(0, r); 126 127 NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb, (void*)this); 128 } 129 130 void TearDown_s() { 131 nr_turn_client_ctx_destroy(&turn_ctx_); 132 if (net_fd_) { 133 NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ); 134 } 135 136 nr_socket_destroy(&buffered_socket_); 137 } 138 139 void TearDown() { 140 test_utils_->SyncDispatchToSTS(WrapRunnable(this, &TurnClient::TearDown_s)); 141 MtransportTest::TearDown(); 142 } 143 144 void Allocate_s() { 145 Init_s(); 146 ASSERT_TRUE(turn_ctx_); 147 148 int r = nr_turn_client_allocate(turn_ctx_, allocate_success_cb, this); 149 ASSERT_EQ(0, r); 150 } 151 152 void Allocate(bool expect_success = true) { 153 test_utils_->SyncDispatchToSTS(WrapRunnable(this, &TurnClient::Allocate_s)); 154 155 if (expect_success) { 156 ASSERT_TRUE_WAIT(allocated_, 5000); 157 } else { 158 PR_Sleep(10000); 159 ASSERT_FALSE(allocated_); 160 } 161 } 162 163 void Allocated() { 164 if (turn_ctx_->state != NR_TURN_CLIENT_STATE_ALLOCATED) { 165 std::cerr << "Allocation failed" << std::endl; 166 return; 167 } 168 allocated_ = true; 169 170 int r; 171 nr_transport_addr addr; 172 173 r = nr_turn_client_get_relayed_address(turn_ctx_, &addr); 174 ASSERT_EQ(0, r); 175 176 relay_addr_ = addr.as_string; 177 178 std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl; 179 } 180 181 void Deallocate_s() { 182 ASSERT_TRUE(turn_ctx_); 183 184 std::cerr << "De-Allocating..." << std::endl; 185 int r = nr_turn_client_deallocate(turn_ctx_); 186 ASSERT_EQ(0, r); 187 } 188 189 void Deallocate() { 190 test_utils_->SyncDispatchToSTS( 191 WrapRunnable(this, &TurnClient::Deallocate_s)); 192 } 193 194 void RequestPermission_s(const std::string& target) { 195 nr_transport_addr addr; 196 int r; 197 198 // Expected pattern here is "IP4:127.0.0.1:3487" 199 ASSERT_EQ(0, target.compare(0, 4, "IP4:")); 200 201 size_t offset = target.rfind(':'); 202 ASSERT_NE(std::string::npos, offset); 203 204 std::string host = target.substr(4, offset - 4); 205 std::string port = target.substr(offset + 1); 206 207 r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()), 208 IPPROTO_UDP, &addr); 209 ASSERT_EQ(0, r); 210 211 r = nr_turn_client_ensure_perm(turn_ctx_, &addr); 212 ASSERT_EQ(0, r); 213 } 214 215 void RequestPermission(const std::string& target) { 216 test_utils_->SyncDispatchToSTS( 217 WrapRunnable(this, &TurnClient::RequestPermission_s, target)); 218 } 219 220 void Readable(NR_SOCKET s, int how, void* arg) { 221 // Re-arm 222 std::cerr << "Socket is readable" << std::endl; 223 NR_ASYNC_WAIT(s, how, socket_readable_cb, arg); 224 225 UCHAR buf[8192]; 226 size_t len_s; 227 nr_transport_addr addr; 228 229 int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr); 230 if (r) { 231 std::cerr << "Error reading from socket" << std::endl; 232 return; 233 } 234 235 ASSERT_LT(len_s, (size_t)INT_MAX); 236 int len = (int)len_s; 237 238 if (nr_is_stun_response_message(buf, len)) { 239 std::cerr << "STUN response" << std::endl; 240 r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr); 241 242 if (r && r != R_REJECTED && r != R_RETRY) { 243 std::cerr << "Error processing STUN: " << r << std::endl; 244 } 245 } else if (nr_is_stun_indication_message(buf, len)) { 246 std::cerr << "STUN indication" << std::endl; 247 248 /* Process the indication */ 249 unsigned char data[NR_STUN_MAX_MESSAGE_SIZE]; 250 size_t datal; 251 nr_transport_addr remote_addr; 252 253 r = nr_turn_client_parse_data_indication( 254 turn_ctx_, &addr, buf, len, data, &datal, sizeof(data), &remote_addr); 255 ASSERT_EQ(0, r); 256 std::cerr << "Received " << datal << " bytes from " 257 << remote_addr.as_string << std::endl; 258 259 received_ += datal; 260 261 for (size_t i = 0; i < datal; i++) { 262 ASSERT_EQ(i & 0xff, data[i]); 263 } 264 } else { 265 if (nr_is_stun_message(buf, len)) { 266 std::cerr << "STUN message of unexpected type" << std::endl; 267 } else { 268 std::cerr << "Not a STUN message" << std::endl; 269 } 270 return; 271 } 272 } 273 274 void SendTo_s(const std::string& target, int expect_return) { 275 nr_transport_addr addr; 276 int r; 277 278 // Expected pattern here is "IP4:127.0.0.1:3487" 279 ASSERT_EQ(0, target.compare(0, 4, "IP4:")); 280 281 size_t offset = target.rfind(':'); 282 ASSERT_NE(std::string::npos, offset); 283 284 std::string host = target.substr(4, offset - 4); 285 std::string port = target.substr(offset + 1); 286 287 r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()), 288 IPPROTO_UDP, &addr); 289 ASSERT_EQ(0, r); 290 291 unsigned char test[100]; 292 for (size_t i = 0; i < sizeof(test); i++) { 293 test[i] = i & 0xff; 294 } 295 296 std::cerr << "Sending test message to " << target << " ..." << std::endl; 297 298 r = nr_turn_client_send_indication(turn_ctx_, test, sizeof(test), 0, &addr); 299 if (expect_return >= 0) { 300 ASSERT_EQ(expect_return, r); 301 } 302 } 303 304 void SendTo(const std::string& target, int expect_return = 0) { 305 test_utils_->SyncDispatchToSTS( 306 WrapRunnable(this, &TurnClient::SendTo_s, target, expect_return)); 307 } 308 309 int received() const { return received_; } 310 311 static void socket_readable_cb(NR_SOCKET s, int how, void* arg) { 312 static_cast<TurnClient*>(arg)->Readable(s, how, arg); 313 } 314 315 static void allocate_success_cb(NR_SOCKET s, int how, void* arg) { 316 static_cast<TurnClient*>(arg)->Allocated(); 317 } 318 319 protected: 320 std::string turn_server_; 321 nr_socket* real_socket_; 322 nr_socket* net_socket_; 323 nr_socket* buffered_socket_; 324 NR_SOCKET net_fd_; 325 nr_turn_client_ctx* turn_ctx_; 326 std::string relay_addr_; 327 bool allocated_; 328 int received_; 329 int protocol_; 330 }; 331 332 TEST_F(TurnClient, Allocate) { 333 if (WarnIfTurnNotConfigured()) return; 334 335 Allocate(); 336 } 337 338 TEST_F(TurnClient, AllocateTcp) { 339 if (WarnIfTurnNotConfigured()) return; 340 341 SetTcp(); 342 Allocate(); 343 } 344 345 TEST_F(TurnClient, AllocateAndHold) { 346 if (WarnIfTurnNotConfigured()) return; 347 348 Allocate(); 349 PR_Sleep(20000); 350 ASSERT_TRUE(turn_ctx_->state == NR_TURN_CLIENT_STATE_ALLOCATED); 351 } 352 353 TEST_F(TurnClient, SendToSelf) { 354 if (WarnIfTurnNotConfigured()) return; 355 356 Allocate(); 357 SendTo(relay_addr_); 358 ASSERT_TRUE_WAIT(received() == 100, 5000); 359 SendTo(relay_addr_); 360 ASSERT_TRUE_WAIT(received() == 200, 1000); 361 } 362 363 TEST_F(TurnClient, SendToSelfTcp) { 364 if (WarnIfTurnNotConfigured()) return; 365 366 SetTcp(); 367 Allocate(); 368 SendTo(relay_addr_); 369 ASSERT_TRUE_WAIT(received() == 100, 5000); 370 SendTo(relay_addr_); 371 ASSERT_TRUE_WAIT(received() == 200, 1000); 372 } 373 374 TEST_F(TurnClient, PermissionDenied) { 375 if (WarnIfTurnNotConfigured()) return; 376 377 Allocate(); 378 RequestPermission(relay_addr_); 379 PR_Sleep(1000); 380 381 /* Fake a 403 response */ 382 nr_turn_permission* perm; 383 perm = STAILQ_FIRST(&turn_ctx_->permissions); 384 ASSERT_TRUE(perm); 385 while (perm) { 386 perm->stun->last_error_code = 403; 387 std::cerr << "Set 403's on permission" << std::endl; 388 perm = STAILQ_NEXT(perm, entry); 389 } 390 391 SendTo(relay_addr_, R_NOT_PERMITTED); 392 ASSERT_TRUE(received() == 0); 393 394 // TODO: We should check if we can still send to a second destination, but 395 // we would need a second TURN client as one client can only handle one 396 // allocation (maybe as part of bug 1128128 ?). 397 } 398 399 TEST_F(TurnClient, DeallocateReceiveFailure) { 400 if (WarnIfTurnNotConfigured()) return; 401 402 Allocate(); 403 SendTo(relay_addr_); 404 ASSERT_TRUE_WAIT(received() == 100, 5000); 405 Deallocate(); 406 turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; 407 SendTo(relay_addr_); 408 PR_Sleep(1000); 409 ASSERT_TRUE(received() == 100); 410 } 411 412 TEST_F(TurnClient, DeallocateReceiveFailureTcp) { 413 if (WarnIfTurnNotConfigured()) return; 414 415 SetTcp(); 416 Allocate(); 417 SendTo(relay_addr_); 418 ASSERT_TRUE_WAIT(received() == 100, 5000); 419 Deallocate(); 420 turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; 421 /* Either the connection got closed by the TURN server already, then the send 422 * is going to fail, which we simply ignore. Or the connection is still alive 423 * and we cand send the data, but it should not get forwarded to us. In either 424 * case we should not receive more data. */ 425 SendTo(relay_addr_, -1); 426 PR_Sleep(1000); 427 ASSERT_TRUE(received() == 100); 428 } 429 430 TEST_F(TurnClient, AllocateDummyServer) { 431 if (WarnIfTurnNotConfigured()) return; 432 433 turn_server_ = kDummyTurnServer; 434 Allocate(false); 435 }