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