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