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 // Some of this code is taken from nricectx.cpp and nricemediastream.cpp
8 // which in turn contains code cut-and-pasted from nICEr. Copyright is:
9
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 "gtest/gtest.h"
43 #include "gtest_utils.h"
44
45 extern "C" {
46 #include "stun_msg.h"
47 #include "ice_ctx.h"
48 #include "ice_peer_ctx.h"
49 #include "nICEr/src/net/transport_addr.h"
50 }
51
52 #include "mtransport_test_utils.h"
53 #include "nricectx.h"
54 #include "nricemediastream.h"
55 #include "runnable_utils.h"
56 #include "test_nr_socket.h"
57
58 namespace mozilla {
59
60 static unsigned int kDefaultTimeout = 7000;
61
62 class IcePeer {
63 public:
IcePeer(const char * name,TestNat * nat,UINT4 flags,MtransportTestUtils * test_utils)64 IcePeer(const char* name, TestNat* nat, UINT4 flags,
65 MtransportTestUtils* test_utils)
66 : name_(name),
67 ice_checking_(false),
68 ice_connected_(false),
69 ice_disconnected_(false),
70 gather_cb_(false),
71 stream_ready_(false),
72 stream_failed_(false),
73 ice_ctx_(nullptr),
74 peer_ctx_(nullptr),
75 nat_(nat),
76 test_utils_(test_utils) {
77 nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ice_ctx_);
78
79 if (nat_) {
80 nr_socket_factory* factory;
81 nat_->create_socket_factory(&factory);
82 nr_ice_ctx_set_socket_factory(ice_ctx_, factory);
83 }
84
85 // Create the handler objects
86 ice_handler_vtbl_ = new nr_ice_handler_vtbl();
87 ice_handler_vtbl_->select_pair = &IcePeer::select_pair;
88 ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready;
89 ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed;
90 ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected;
91 ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd;
92 ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking;
93 ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected;
94
95 ice_handler_ = new nr_ice_handler();
96 ice_handler_->vtbl = ice_handler_vtbl_;
97 ice_handler_->obj = this;
98
99 nr_ice_peer_ctx_create(ice_ctx_, ice_handler_,
100 const_cast<char*>(name_.c_str()), &peer_ctx_);
101
102 nr_ice_add_media_stream(ice_ctx_, const_cast<char*>(name_.c_str()), "ufrag",
103 "pass", 2, &ice_media_stream_);
104 EXPECT_EQ(2UL, GetStreamAttributes().size());
105
106 nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_);
107 }
108
~IcePeer()109 virtual ~IcePeer() { Destroy(); }
110
Destroy()111 void Destroy() {
112 test_utils_->sts_target()->Dispatch(WrapRunnable(this, &IcePeer::Destroy_s),
113 NS_DISPATCH_SYNC);
114 }
115
Destroy_s()116 void Destroy_s() {
117 nr_ice_peer_ctx_destroy(&peer_ctx_);
118 delete ice_handler_;
119 delete ice_handler_vtbl_;
120 nr_ice_ctx_destroy(&ice_ctx_);
121 }
122
Gather(bool default_route_only=false)123 void Gather(bool default_route_only = false) {
124 test_utils_->sts_target()->Dispatch(
125 WrapRunnable(this, &IcePeer::Gather_s, default_route_only),
126 NS_DISPATCH_SYNC);
127 }
128
Gather_s(bool default_route_only=false)129 void Gather_s(bool default_route_only = false) {
130 int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this);
131 ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK);
132 }
133
GetStreamAttributes()134 std::vector<std::string> GetStreamAttributes() {
135 std::vector<std::string> attributes;
136 test_utils_->sts_target()->Dispatch(
137 WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s),
138 NS_DISPATCH_SYNC);
139 return attributes;
140 }
141
GetStreamAttributes_s()142 std::vector<std::string> GetStreamAttributes_s() {
143 char** attrs = nullptr;
144 int attrct;
145 std::vector<std::string> ret;
146
147 int r =
148 nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct);
149 EXPECT_EQ(0, r);
150
151 for (int i = 0; i < attrct; i++) {
152 ret.push_back(std::string(attrs[i]));
153 RFREE(attrs[i]);
154 }
155 RFREE(attrs);
156
157 return ret;
158 }
159
GetGlobalAttributes()160 std::vector<std::string> GetGlobalAttributes() {
161 std::vector<std::string> attributes;
162 test_utils_->sts_target()->Dispatch(
163 WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s),
164 NS_DISPATCH_SYNC);
165 return attributes;
166 }
167
GetGlobalAttributes_s()168 std::vector<std::string> GetGlobalAttributes_s() {
169 char** attrs = nullptr;
170 int attrct;
171 std::vector<std::string> ret;
172
173 nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct);
174
175 for (int i = 0; i < attrct; i++) {
176 ret.push_back(std::string(attrs[i]));
177 RFREE(attrs[i]);
178 }
179 RFREE(attrs);
180
181 return ret;
182 }
183
ParseGlobalAttributes(std::vector<std::string> attrs)184 void ParseGlobalAttributes(std::vector<std::string> attrs) {
185 std::vector<char*> attrs_in;
186 attrs_in.reserve(attrs.size());
187 for (auto& attr : attrs) {
188 attrs_in.push_back(const_cast<char*>(attr.c_str()));
189 }
190
191 int r = nr_ice_peer_ctx_parse_global_attributes(
192 peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
193 ASSERT_EQ(0, r);
194 }
195
SetControlling(bool controlling)196 void SetControlling(bool controlling) {
197 peer_ctx_->controlling = controlling ? 1 : 0;
198 }
199
SetRemoteAttributes(std::vector<std::string> attributes)200 void SetRemoteAttributes(std::vector<std::string> attributes) {
201 test_utils_->sts_target()->Dispatch(
202 WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes),
203 NS_DISPATCH_SYNC);
204 }
205
SetRemoteAttributes_s(std::vector<std::string> attributes)206 void SetRemoteAttributes_s(std::vector<std::string> attributes) {
207 int r;
208
209 std::vector<char*> attrs;
210 attrs.reserve(attributes.size());
211 for (auto& attr : attributes) {
212 attrs.push_back(const_cast<char*>(attr.c_str()));
213 }
214
215 if (!attrs.empty()) {
216 r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_,
217 &attrs[0], attrs.size());
218 ASSERT_EQ(0, r);
219 }
220 }
221
StartChecks()222 void StartChecks() {
223 test_utils_->sts_target()->Dispatch(
224 WrapRunnable(this, &IcePeer::StartChecks_s), NS_DISPATCH_SYNC);
225 }
226
StartChecks_s()227 void StartChecks_s() {
228 int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_);
229 ASSERT_EQ(0, r);
230
231 r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1);
232 ASSERT_EQ(0, r);
233 }
234
235 // Handler callbacks
select_pair(void * obj,nr_ice_media_stream * stream,int component_id,nr_ice_cand_pair ** potentials,int potential_ct)236 static int select_pair(void* obj, nr_ice_media_stream* stream,
237 int component_id, nr_ice_cand_pair** potentials,
238 int potential_ct) {
239 return 0;
240 }
241
stream_ready(void * obj,nr_ice_media_stream * stream)242 static int stream_ready(void* obj, nr_ice_media_stream* stream) {
243 IcePeer* peer = static_cast<IcePeer*>(obj);
244 peer->stream_ready_ = true;
245 return 0;
246 }
247
stream_failed(void * obj,nr_ice_media_stream * stream)248 static int stream_failed(void* obj, nr_ice_media_stream* stream) {
249 IcePeer* peer = static_cast<IcePeer*>(obj);
250 peer->stream_failed_ = true;
251 return 0;
252 }
253
ice_checking(void * obj,nr_ice_peer_ctx * pctx)254 static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) {
255 IcePeer* peer = static_cast<IcePeer*>(obj);
256 peer->ice_checking_ = true;
257 return 0;
258 }
259
ice_connected(void * obj,nr_ice_peer_ctx * pctx)260 static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) {
261 IcePeer* peer = static_cast<IcePeer*>(obj);
262 peer->ice_connected_ = true;
263 return 0;
264 }
265
ice_disconnected(void * obj,nr_ice_peer_ctx * pctx)266 static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) {
267 IcePeer* peer = static_cast<IcePeer*>(obj);
268 peer->ice_disconnected_ = true;
269 return 0;
270 }
271
msg_recvd(void * obj,nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,int component_id,UCHAR * msg,int len)272 static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx,
273 nr_ice_media_stream* stream, int component_id,
274 UCHAR* msg, int len) {
275 return 0;
276 }
277
gather_cb(NR_SOCKET s,int h,void * arg)278 static void gather_cb(NR_SOCKET s, int h, void* arg) {
279 IcePeer* peer = static_cast<IcePeer*>(arg);
280 peer->gather_cb_ = true;
281 }
282
283 std::string name_;
284
285 bool ice_checking_;
286 bool ice_connected_;
287 bool ice_disconnected_;
288 bool gather_cb_;
289 bool stream_ready_;
290 bool stream_failed_;
291
292 nr_ice_ctx* ice_ctx_;
293 nr_ice_handler* ice_handler_;
294 nr_ice_handler_vtbl* ice_handler_vtbl_;
295 nr_ice_media_stream* ice_media_stream_;
296 nr_ice_peer_ctx* peer_ctx_;
297 TestNat* nat_;
298 MtransportTestUtils* test_utils_;
299 };
300
301 class TestNrSocketIceUnitTest : public ::testing::Test {
302 public:
SetUp()303 void SetUp() override {
304 NSS_NoDB_Init(nullptr);
305 NSS_SetDomesticPolicy();
306
307 test_utils_ = new MtransportTestUtils();
308 test_utils2_ = new MtransportTestUtils();
309
310 NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
311 }
312
TearDown()313 void TearDown() override {
314 delete test_utils_;
315 delete test_utils2_;
316 }
317
318 MtransportTestUtils* test_utils_;
319 MtransportTestUtils* test_utils2_;
320 };
321
TEST_F(TestNrSocketIceUnitTest,TestIcePeer)322 TEST_F(TestNrSocketIceUnitTest, TestIcePeer) {
323 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
324 test_utils_);
325 ASSERT_NE(peer.ice_ctx_, nullptr);
326 ASSERT_NE(peer.peer_ctx_, nullptr);
327 ASSERT_NE(peer.ice_media_stream_, nullptr);
328 ASSERT_EQ(2UL, peer.GetStreamAttributes().size())
329 << "Should have ice-ufrag and ice-pwd";
330 peer.Gather();
331 ASSERT_LT(2UL, peer.GetStreamAttributes().size())
332 << "Should have ice-ufrag, ice-pwd, and at least one candidate.";
333 }
334
TEST_F(TestNrSocketIceUnitTest,TestIcePeersNoNAT)335 TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) {
336 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
337 test_utils_);
338 IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
339 test_utils2_);
340 peer.SetControlling(true);
341 peer2.SetControlling(false);
342
343 peer.Gather();
344 peer2.Gather();
345 std::vector<std::string> attrs = peer.GetGlobalAttributes();
346 peer2.ParseGlobalAttributes(attrs);
347 std::vector<std::string> attributes = peer.GetStreamAttributes();
348 peer2.SetRemoteAttributes(attributes);
349
350 attrs = peer2.GetGlobalAttributes();
351 peer.ParseGlobalAttributes(attrs);
352 attributes = peer2.GetStreamAttributes();
353 peer.SetRemoteAttributes(attributes);
354 peer2.StartChecks();
355 peer.StartChecks();
356
357 ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
358 ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
359 }
360
TEST_F(TestNrSocketIceUnitTest,TestIcePeersPacketLoss)361 TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) {
362 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
363 test_utils_);
364
365 RefPtr<TestNat> nat(new TestNat);
366 class NatDelegate : public TestNat::NatDelegate {
367 public:
368 NatDelegate() : messages(0) {}
369
370 int on_read(TestNat* nat, void* buf, size_t maxlen, size_t* len) override {
371 return 0;
372 }
373
374 int on_sendto(TestNat* nat, const void* msg, size_t len, int flags,
375 const nr_transport_addr* to) override {
376 ++messages;
377 // 25% packet loss
378 if (messages % 4 == 0) {
379 return 1;
380 }
381 return 0;
382 }
383
384 int on_write(TestNat* nat, const void* msg, size_t len,
385 size_t* written) override {
386 return 0;
387 }
388
389 int messages;
390 } delegate;
391 nat->nat_delegate_ = &delegate;
392
393 IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
394 test_utils2_);
395 peer.SetControlling(true);
396 peer2.SetControlling(false);
397
398 peer.Gather();
399 peer2.Gather();
400 std::vector<std::string> attrs = peer.GetGlobalAttributes();
401 peer2.ParseGlobalAttributes(attrs);
402 std::vector<std::string> attributes = peer.GetStreamAttributes();
403 peer2.SetRemoteAttributes(attributes);
404
405 attrs = peer2.GetGlobalAttributes();
406 peer.ParseGlobalAttributes(attrs);
407 attributes = peer2.GetStreamAttributes();
408 peer.SetRemoteAttributes(attributes);
409 peer2.StartChecks();
410 peer.StartChecks();
411
412 ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
413 ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
414 }
415
416 } // namespace mozilla
417