Bumping manifests a=b2g-bump
[gecko.git] / media / mtransport / test / turn_unittest.cpp
blob9bc523e98f4e4a4637ceee8344f2fda66072ce8f
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/. */
7 // Original author: ekr@rtfm.com
9 // Some code copied from nICEr. License is:
11 Copyright (c) 2007, Adobe Systems, Incorporated
12 All rights reserved.
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions are
16 met:
18 * Redistributions of source code must retain the above copyright
19 notice, this list of conditions and the following disclaimer.
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.
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.
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.
42 #include <stdlib.h>
43 #include <iostream>
45 #include "sigslot.h"
47 #include "logging.h"
48 #include "nspr.h"
49 #include "nss.h"
50 #include "ssl.h"
52 #include "mozilla/Scoped.h"
53 #include "nsThreadUtils.h"
54 #include "nsXPCOM.h"
56 #include "mtransport_test_utils.h"
57 #include "runnable_utils.h"
59 #define GTEST_HAS_RTTI 0
60 #include "gtest/gtest.h"
61 #include "gtest_utils.h"
63 // nICEr includes
64 extern "C" {
65 #include "nr_api.h"
66 #include "registry.h"
67 #include "async_timer.h"
68 #include "r_crc32.h"
69 #include "ice_util.h"
70 #include "transport_addr.h"
71 #include "nr_crypto.h"
72 #include "nr_socket.h"
73 #include "nr_socket_local.h"
74 #include "nr_socket_buffered_stun.h"
75 #include "stun_client_ctx.h"
76 #include "turn_client_ctx.h"
79 #include "nricemediastream.h"
80 #include "nricectx.h"
83 MtransportTestUtils *test_utils;
85 using namespace mozilla;
87 std::string g_turn_server;
88 std::string g_turn_user;
89 std::string g_turn_password;
91 std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737
93 class TurnClient : public ::testing::Test {
94 public:
95 TurnClient()
96 : turn_server_(g_turn_server),
97 real_socket_(nullptr),
98 net_socket_(nullptr),
99 buffered_socket_(nullptr),
100 net_fd_(nullptr),
101 turn_ctx_(nullptr),
102 allocated_(false),
103 received_(0),
104 protocol_(IPPROTO_UDP) {
107 ~TurnClient() {
110 void SetTcp() {
111 protocol_ = IPPROTO_TCP;
114 void Init_s() {
115 int r;
116 nr_transport_addr addr;
117 r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
118 ASSERT_EQ(0, r);
120 r = nr_socket_local_create(&addr, &real_socket_);
121 ASSERT_EQ(0, r);
123 if (protocol_ == IPPROTO_TCP) {
124 int r =
125 nr_socket_buffered_stun_create(real_socket_, 100000,
126 &buffered_socket_);
127 ASSERT_EQ(0, r);
128 net_socket_ = buffered_socket_;
129 } else {
130 net_socket_ = real_socket_;
133 r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478,
134 protocol_, &addr);
135 ASSERT_EQ(0, r);
137 std::vector<unsigned char> password_vec(
138 g_turn_password.begin(), g_turn_password.end());
139 Data password;
140 INIT_DATA(password, &password_vec[0], password_vec.size());
141 r = nr_turn_client_ctx_create("test", net_socket_,
142 g_turn_user.c_str(),
143 &password,
144 &addr, &turn_ctx_);
145 ASSERT_EQ(0, r);
147 r = nr_socket_getfd(net_socket_, &net_fd_);
148 ASSERT_EQ(0, r);
150 NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
151 (void *)this);
154 void TearDown_s() {
155 nr_turn_client_ctx_destroy(&turn_ctx_);
156 if (net_fd_) {
157 NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
160 nr_socket_destroy(&buffered_socket_);
163 void TearDown() {
164 RUN_ON_THREAD(test_utils->sts_target(),
165 WrapRunnable(this, &TurnClient::TearDown_s),
166 NS_DISPATCH_SYNC);
169 void Allocate_s() {
170 Init_s();
171 ASSERT_TRUE(turn_ctx_);
173 int r = nr_turn_client_allocate(turn_ctx_,
174 allocate_success_cb,
175 this);
176 ASSERT_EQ(0, r);
179 void Allocate(bool expect_success=true) {
180 RUN_ON_THREAD(test_utils->sts_target(),
181 WrapRunnable(this, &TurnClient::Allocate_s),
182 NS_DISPATCH_SYNC);
184 if (expect_success) {
185 ASSERT_TRUE_WAIT(allocated_, 5000);
187 else {
188 PR_Sleep(10000);
189 ASSERT_FALSE(allocated_);
193 void Allocated() {
194 if (turn_ctx_->state!=NR_TURN_CLIENT_STATE_ALLOCATED) {
195 std::cerr << "Allocation failed" << std::endl;
196 return;
198 allocated_ = true;
200 int r;
201 nr_transport_addr addr;
203 r = nr_turn_client_get_relayed_address(turn_ctx_, &addr);
204 ASSERT_EQ(0, r);
206 relay_addr_ = addr.as_string;
208 std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl;
211 void Readable(NR_SOCKET s, int how, void *arg) {
212 // Re-arm
213 std::cerr << "Socket is readable" << std::endl;
214 NR_ASYNC_WAIT(s, how, socket_readable_cb, arg);
216 UCHAR buf[8192];
217 size_t len_s;
218 nr_transport_addr addr;
220 int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr);
221 if (r) {
222 std::cerr << "Error reading from socket" << std::endl;
223 return;
226 ASSERT_LT(len_s, (size_t)INT_MAX);
227 int len = (int)len_s;
229 if (nr_is_stun_response_message(buf, len)) {
230 std::cerr << "STUN response" << std::endl;
231 r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr);
233 if (r && r != R_REJECTED && r != R_RETRY) {
234 std::cerr << "Error processing STUN: " << r << std::endl;
236 } else if (nr_is_stun_indication_message(buf, len)) {
237 std::cerr << "STUN indication" << std::endl;
239 /* Process the indication */
240 unsigned char data[NR_STUN_MAX_MESSAGE_SIZE];
241 size_t datal;
242 nr_transport_addr remote_addr;
244 r = nr_turn_client_parse_data_indication(turn_ctx_, &addr,
245 buf, len,
246 data, &datal, sizeof(data),
247 &remote_addr);
248 ASSERT_EQ(0, r);
249 std::cerr << "Received " << datal << " bytes from "
250 << remote_addr.as_string << std::endl;
252 received_ += datal;
254 for (size_t i=0; i < datal; i++) {
255 ASSERT_EQ(i & 0xff, data[i]);
258 else {
259 if (nr_is_stun_message(buf, len)) {
260 std::cerr << "STUN message of unexpected type" << std::endl;
261 } else {
262 std::cerr << "Not a STUN message" << std::endl;
264 return;
268 void SendTo_s(const std::string& target) {
269 nr_transport_addr addr;
270 int r;
272 // Expected pattern here is "IP4:127.0.0.1:3487"
273 ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
275 size_t offset = target.rfind(':');
276 ASSERT_NE(offset, std::string::npos);
278 std::string host = target.substr(4, offset - 4);
279 std::string port = target.substr(offset + 1);
281 r = nr_ip4_str_port_to_transport_addr(host.c_str(),
282 atoi(port.c_str()),
283 IPPROTO_UDP,
284 &addr);
285 ASSERT_EQ(0, r);
287 unsigned char test[100];
288 for (size_t i=0; i<sizeof(test); i++) {
289 test[i] = i & 0xff;
292 r = nr_turn_client_send_indication(turn_ctx_,
293 test, sizeof(test), 0,
294 &addr);
295 ASSERT_EQ(0, r);
298 void SendTo(const std::string& target) {
299 RUN_ON_THREAD(test_utils->sts_target(),
300 WrapRunnable(this, &TurnClient::SendTo_s, target),
301 NS_DISPATCH_SYNC);
304 int received() const { return received_; }
306 static void socket_readable_cb(NR_SOCKET s, int how, void *arg) {
307 static_cast<TurnClient *>(arg)->Readable(s, how, arg);
310 static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
311 static_cast<TurnClient *>(arg)->Allocated();
314 protected:
315 std::string turn_server_;
316 nr_socket *real_socket_;
317 nr_socket *net_socket_;
318 nr_socket *buffered_socket_;
319 NR_SOCKET net_fd_;
320 nr_turn_client_ctx *turn_ctx_;
321 std::string relay_addr_;
322 bool allocated_;
323 int received_;
324 int protocol_;
327 TEST_F(TurnClient, Allocate) {
328 Allocate();
331 TEST_F(TurnClient, AllocateTcp) {
332 SetTcp();
333 Allocate();
336 TEST_F(TurnClient, AllocateAndHold) {
337 Allocate();
338 PR_Sleep(20000);
341 TEST_F(TurnClient, SendToSelf) {
342 Allocate();
343 SendTo(relay_addr_);
344 ASSERT_TRUE_WAIT(received() == 100, 1000);
345 PR_Sleep(10000); // Wait 10 seconds to make sure the
346 // CreatePermission has time to complete/fail.
347 SendTo(relay_addr_);
348 ASSERT_TRUE_WAIT(received() == 200, 1000);
352 TEST_F(TurnClient, SendToSelfTcp) {
353 SetTcp();
354 Allocate();
355 SendTo(relay_addr_);
356 ASSERT_TRUE_WAIT(received() == 100, 1000);
357 PR_Sleep(10000); // Wait 10 seconds to make sure the
358 // CreatePermission has time to complete/fail.
359 SendTo(relay_addr_);
360 ASSERT_TRUE_WAIT(received() == 200, 1000);
363 TEST_F(TurnClient, AllocateDummyServer) {
364 turn_server_ = kDummyTurnServer;
365 Allocate(false);
368 static std::string get_environment(const char *name) {
369 char *value = getenv(name);
371 if (!value)
372 return "";
374 return value;
377 int main(int argc, char **argv)
379 g_turn_server = get_environment("TURN_SERVER_ADDRESS");
380 g_turn_user = get_environment("TURN_SERVER_USER");
381 g_turn_password = get_environment("TURN_SERVER_PASSWORD");
383 if (g_turn_server.empty() ||
384 g_turn_user.empty(),
385 g_turn_password.empty()) {
386 printf(
387 "Set TURN_SERVER_ADDRESS, TURN_SERVER_USER, and TURN_SERVER_PASSWORD\n"
388 "environment variables to run this test\n");
389 return 0;
392 nr_transport_addr addr;
393 if (nr_ip4_str_port_to_transport_addr(g_turn_server.c_str(), 3478,
394 IPPROTO_UDP, &addr)) {
395 printf("Invalid TURN_SERVER_ADDRESS \"%s\". Only IP numbers supported.\n",
396 g_turn_server.c_str());
397 return 0;
400 test_utils = new MtransportTestUtils();
401 NSS_NoDB_Init(nullptr);
402 NSS_SetDomesticPolicy();
404 // Set up the ICE registry, etc.
405 // TODO(ekr@rtfm.com): Clean up
406 std::string dummy("dummy");
407 RUN_ON_THREAD(test_utils->sts_target(),
408 WrapRunnableNM(&NrIceCtx::Create,
409 dummy, false, false, false),
410 NS_DISPATCH_SYNC);
412 // Start the tests
413 ::testing::InitGoogleTest(&argc, argv);
415 int rv = RUN_ALL_TESTS();
416 delete test_utils;
417 return rv;