media/mtransport/test/turn_unittest.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:2ac13b216f0a
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 #include <iostream>
44
45 #include "sigslot.h"
46
47 #include "logging.h"
48 #include "nspr.h"
49 #include "nss.h"
50 #include "ssl.h"
51
52 #include "mozilla/Scoped.h"
53 #include "nsThreadUtils.h"
54 #include "nsXPCOM.h"
55
56 #include "mtransport_test_utils.h"
57 #include "runnable_utils.h"
58
59 #define GTEST_HAS_RTTI 0
60 #include "gtest/gtest.h"
61 #include "gtest_utils.h"
62
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"
77 }
78
79 #include "nricemediastream.h"
80 #include "nricectx.h"
81
82
83 MtransportTestUtils *test_utils;
84
85 using namespace mozilla;
86
87 std::string g_turn_server;
88 std::string g_turn_user;
89 std::string g_turn_password;
90
91 std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737
92
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) {
105 }
106
107 ~TurnClient() {
108 }
109
110 void SetTcp() {
111 protocol_ = IPPROTO_TCP;
112 }
113
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);
119
120 r = nr_socket_local_create(&addr, &real_socket_);
121 ASSERT_EQ(0, r);
122
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_;
131 }
132
133 r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478,
134 protocol_, &addr);
135 ASSERT_EQ(0, r);
136
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);
146
147 r = nr_socket_getfd(net_socket_, &net_fd_);
148 ASSERT_EQ(0, r);
149
150 NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
151 (void *)this);
152 }
153
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);
158 }
159
160 nr_socket_destroy(&buffered_socket_);
161 }
162
163 void TearDown() {
164 RUN_ON_THREAD(test_utils->sts_target(),
165 WrapRunnable(this, &TurnClient::TearDown_s),
166 NS_DISPATCH_SYNC);
167 }
168
169 void Allocate_s() {
170 Init_s();
171 ASSERT_TRUE(turn_ctx_);
172
173 int r = nr_turn_client_allocate(turn_ctx_,
174 allocate_success_cb,
175 this);
176 ASSERT_EQ(0, r);
177 }
178
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);
183
184 if (expect_success) {
185 ASSERT_TRUE_WAIT(allocated_, 5000);
186 }
187 else {
188 PR_Sleep(10000);
189 ASSERT_FALSE(allocated_);
190 }
191 }
192
193 void Allocated() {
194 if (turn_ctx_->state!=NR_TURN_CLIENT_STATE_ALLOCATED) {
195 std::cerr << "Allocation failed" << std::endl;
196 return;
197 }
198 allocated_ = true;
199
200 int r;
201 nr_transport_addr addr;
202
203 r = nr_turn_client_get_relayed_address(turn_ctx_, &addr);
204 ASSERT_EQ(0, r);
205
206 relay_addr_ = addr.as_string;
207
208 std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl;
209 }
210
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);
215
216 UCHAR buf[8192];
217 size_t len_s;
218 nr_transport_addr addr;
219
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;
224 }
225
226 ASSERT_LT(len_s, (size_t)INT_MAX);
227 int len = (int)len_s;
228
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);
232
233 if (r && r != R_REJECTED && r != R_RETRY) {
234 std::cerr << "Error processing STUN: " << r << std::endl;
235 }
236 } else if (nr_is_stun_indication_message(buf, len)) {
237 std::cerr << "STUN indication" << std::endl;
238
239 /* Process the indication */
240 unsigned char data[NR_STUN_MAX_MESSAGE_SIZE];
241 size_t datal;
242 nr_transport_addr remote_addr;
243
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;
251
252 received_ += datal;
253
254 for (size_t i=0; i < datal; i++) {
255 ASSERT_EQ(i & 0xff, data[i]);
256 }
257 }
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;
263 }
264 return;
265 }
266 }
267
268 void SendTo_s(const std::string& target) {
269 nr_transport_addr addr;
270 int r;
271
272 // Expected pattern here is "IP4:127.0.0.1:3487"
273 ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
274
275 size_t offset = target.rfind(':');
276 ASSERT_NE(offset, std::string::npos);
277
278 std::string host = target.substr(4, offset - 4);
279 std::string port = target.substr(offset + 1);
280
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);
286
287 unsigned char test[100];
288 for (size_t i=0; i<sizeof(test); i++) {
289 test[i] = i & 0xff;
290 }
291
292 r = nr_turn_client_send_indication(turn_ctx_,
293 test, sizeof(test), 0,
294 &addr);
295 ASSERT_EQ(0, r);
296 }
297
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);
302 }
303
304 int received() const { return received_; }
305
306 static void socket_readable_cb(NR_SOCKET s, int how, void *arg) {
307 static_cast<TurnClient *>(arg)->Readable(s, how, arg);
308 }
309
310 static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
311 static_cast<TurnClient *>(arg)->Allocated();
312 }
313
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_;
325 };
326
327 TEST_F(TurnClient, Allocate) {
328 Allocate();
329 }
330
331 TEST_F(TurnClient, AllocateTcp) {
332 SetTcp();
333 Allocate();
334 }
335
336 TEST_F(TurnClient, AllocateAndHold) {
337 Allocate();
338 PR_Sleep(20000);
339 }
340
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);
349 }
350
351
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);
361 }
362
363 TEST_F(TurnClient, AllocateDummyServer) {
364 turn_server_ = kDummyTurnServer;
365 Allocate(false);
366 }
367
368 static std::string get_environment(const char *name) {
369 char *value = getenv(name);
370
371 if (!value)
372 return "";
373
374 return value;
375 }
376
377 int main(int argc, char **argv)
378 {
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");
382
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;
390 }
391 {
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;
398 }
399 }
400 test_utils = new MtransportTestUtils();
401 NSS_NoDB_Init(nullptr);
402 NSS_SetDomesticPolicy();
403
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),
410 NS_DISPATCH_SYNC);
411
412 // Start the tests
413 ::testing::InitGoogleTest(&argc, argv);
414
415 int rv = RUN_ALL_TESTS();
416 delete test_utils;
417 return rv;
418 }

mercurial