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