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/. */
7 // Original author: ekr@rtfm.com
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
12 Copyright (c) 2007, Adobe Systems, Incorporated
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
22 * Redistributions in binary form must reproduce the above copyright
23 notice, this list of conditions and the following disclaimer in the
24 documentation and/or other materials provided with the distribution.
26 * Neither the name of Adobe Systems, Network Resonance nor the names of its
27 contributors may be used to endorse or promote products derived from
28 this software without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49 #include "mozilla/Scoped.h"
55 #include "async_timer.h"
57 #include "transport_addr.h"
58 #include "nr_crypto.h"
59 #include "nr_socket.h"
60 #include "nr_socket_local.h"
61 #include "stun_client_ctx.h"
62 #include "stun_server_ctx.h"
64 #include "ice_candidate.h"
65 #include "ice_handler.h"
70 #include "nricemediastream.h"
74 MOZ_MTLOG_MODULE("mtransport")
76 static bool ToNrIceAddr(nr_transport_addr
&addr
,
79 char addrstring
[INET6_ADDRSTRLEN
+ 1];
81 r
= nr_transport_addr_get_addrstring(&addr
, addrstring
, sizeof(addrstring
));
84 out
->host
= addrstring
;
87 r
= nr_transport_addr_get_port(&addr
, &port
);
93 switch (addr
.protocol
) {
95 out
->transport
= kNrIceTransportTcp
;
98 out
->transport
= kNrIceTransportUdp
;
108 static bool ToNrIceCandidate(const nr_ice_candidate
& candc
,
109 NrIceCandidate
* out
) {
112 // Const-cast because the internal nICEr code isn't const-correct.
113 nr_ice_candidate
*cand
= const_cast<nr_ice_candidate
*>(&candc
);
115 if (!ToNrIceAddr(cand
->addr
, &out
->cand_addr
))
119 nr_transport_addr addr
;
120 r
= nr_socket_getaddr(cand
->isock
->sock
, &addr
);
124 if (!ToNrIceAddr(addr
, &out
->local_addr
))
128 NrIceCandidate::Type type
;
130 switch (cand
->type
) {
132 type
= NrIceCandidate::ICE_HOST
;
134 case SERVER_REFLEXIVE
:
135 type
= NrIceCandidate::ICE_SERVER_REFLEXIVE
;
138 type
= NrIceCandidate::ICE_PEER_REFLEXIVE
;
141 type
= NrIceCandidate::ICE_RELAYED
;
148 out
->codeword
= candc
.codeword
;
152 // Make an NrIceCandidate from the candidate |cand|.
153 // This is not a member fxn because we want to hide the
154 // defn of nr_ice_candidate but we pass by reference.
155 static NrIceCandidate
* MakeNrIceCandidate(const nr_ice_candidate
& candc
) {
156 ScopedDeletePtr
<NrIceCandidate
> out(new NrIceCandidate());
158 if (!ToNrIceCandidate(candc
, out
)) {
165 RefPtr
<NrIceMediaStream
>
166 NrIceMediaStream::Create(NrIceCtx
*ctx
,
167 const std::string
& name
,
169 RefPtr
<NrIceMediaStream
> stream
=
170 new NrIceMediaStream(ctx
, name
, components
);
172 int r
= nr_ice_add_media_stream(ctx
->ctx(),
173 const_cast<char *>(name
.c_str()),
174 components
, &stream
->stream_
);
176 MOZ_MTLOG(ML_ERROR
, "Couldn't create ICE media stream for '"
184 NrIceMediaStream::~NrIceMediaStream() {
185 // We do not need to destroy anything. All major resources
186 // are attached to the ice ctx.
189 nsresult
NrIceMediaStream::ParseAttributes(std::vector
<std::string
>&
192 return NS_ERROR_FAILURE
;
194 std::vector
<char *> attributes_in
;
196 for (size_t i
=0; i
<attributes
.size(); ++i
) {
197 attributes_in
.push_back(const_cast<char *>(attributes
[i
].c_str()));
200 // Still need to call nr_ice_ctx_parse_stream_attributes.
201 int r
= nr_ice_peer_ctx_parse_stream_attributes(ctx_
->peer(),
203 attributes_in
.size() ?
204 &attributes_in
[0] : nullptr,
205 attributes_in
.size());
207 MOZ_MTLOG(ML_ERROR
, "Couldn't parse attributes for stream "
209 return NS_ERROR_FAILURE
;
212 has_parsed_attrs_
= true;
216 // Parse trickle ICE candidate
217 nsresult
NrIceMediaStream::ParseTrickleCandidate(const std::string
& candidate
) {
220 MOZ_MTLOG(ML_DEBUG
, "NrIceCtx(" << ctx_
->name() << ")/STREAM(" <<
221 name() << ") : parsing trickle candidate " << candidate
);
223 r
= nr_ice_peer_ctx_parse_trickle_candidate(ctx_
->peer(),
229 if (r
== R_ALREADY
) {
230 MOZ_MTLOG(ML_ERROR
, "Trickle candidates are redundant for stream '"
231 << name_
<< "' because it is completed");
234 MOZ_MTLOG(ML_ERROR
, "Couldn't parse trickle candidate for stream '"
236 return NS_ERROR_FAILURE
;
243 // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
244 nsresult
NrIceMediaStream::GetActivePair(int component
,
245 NrIceCandidate
**localp
,
246 NrIceCandidate
**remotep
) {
248 nr_ice_candidate
*local_int
;
249 nr_ice_candidate
*remote_int
;
251 r
= nr_ice_media_stream_get_active(ctx_
->peer(),
254 &local_int
, &remote_int
);
255 // If result is R_REJECTED then component is unpaired or disabled.
257 return NS_ERROR_NOT_AVAILABLE
;
260 return NS_ERROR_FAILURE
;
262 ScopedDeletePtr
<NrIceCandidate
> local(
263 MakeNrIceCandidate(*local_int
));
265 return NS_ERROR_FAILURE
;
267 ScopedDeletePtr
<NrIceCandidate
> remote(
268 MakeNrIceCandidate(*remote_int
));
270 return NS_ERROR_FAILURE
;
273 *localp
= local
.forget();
275 *remotep
= remote
.forget();
281 nsresult
NrIceMediaStream::GetCandidatePairs(std::vector
<NrIceCandidatePair
>*
283 MOZ_ASSERT(out_pairs
);
285 // Get the check_list on the peer stream (this is where the check_list
286 // actually lives, not in stream_)
287 nr_ice_media_stream
* peer_stream
;
288 int r
= nr_ice_peer_ctx_find_pstream(ctx_
->peer(), stream_
, &peer_stream
);
290 return NS_ERROR_FAILURE
;
293 nr_ice_cand_pair
*p1
;
296 TAILQ_FOREACH(p1
, &peer_stream
->check_list
, entry
) {
298 MOZ_ASSERT(p1
->local
);
299 MOZ_ASSERT(p1
->remote
);
300 NrIceCandidatePair pair
;
303 case NR_ICE_PAIR_STATE_FROZEN
:
304 pair
.state
= NrIceCandidatePair::State::STATE_FROZEN
;
306 case NR_ICE_PAIR_STATE_WAITING
:
307 pair
.state
= NrIceCandidatePair::State::STATE_WAITING
;
309 case NR_ICE_PAIR_STATE_IN_PROGRESS
:
310 pair
.state
= NrIceCandidatePair::State::STATE_IN_PROGRESS
;
312 case NR_ICE_PAIR_STATE_FAILED
:
313 pair
.state
= NrIceCandidatePair::State::STATE_FAILED
;
315 case NR_ICE_PAIR_STATE_SUCCEEDED
:
316 pair
.state
= NrIceCandidatePair::State::STATE_SUCCEEDED
;
318 case NR_ICE_PAIR_STATE_CANCELLED
:
319 pair
.state
= NrIceCandidatePair::State::STATE_CANCELLED
;
325 pair
.priority
= p1
->priority
;
326 pair
.nominated
= p1
->peer_nominated
|| p1
->nominated
;
327 pair
.selected
= p1
->remote
->component
&&
328 p1
->remote
->component
->active
== p1
;
329 pair
.codeword
= p1
->codeword
;
331 if (!ToNrIceCandidate(*(p1
->local
), &pair
.local
) ||
332 !ToNrIceCandidate(*(p1
->remote
), &pair
.remote
)) {
333 return NS_ERROR_FAILURE
;
336 out_pairs
->push_back(pair
);
342 nsresult
NrIceMediaStream::GetDefaultCandidate(
343 NrIceCandidate
* candidate
) const {
345 nr_ice_candidate
*cand
;
347 int r
= nr_ice_media_stream_get_default_candidate(stream_
, 1, &cand
);
349 MOZ_MTLOG(ML_ERROR
, "Couldn't get default ICE candidate for '"
351 return NS_ERROR_FAILURE
;
354 if (!ToNrIceCandidate(*cand
, candidate
)) {
355 MOZ_MTLOG(ML_ERROR
, "Failed to convert default ICE candidate for '"
357 return NS_ERROR_FAILURE
;
363 std::vector
<std::string
> NrIceMediaStream::GetCandidates() const {
367 std::vector
<std::string
> ret
;
369 r
= nr_ice_media_stream_get_attributes(stream_
,
372 MOZ_MTLOG(ML_ERROR
, "Couldn't get ICE candidates for '"
377 for (int i
=0; i
<attrct
; i
++) {
378 ret
.push_back(attrs
[i
]);
387 static nsresult
GetCandidatesFromStream(
388 nr_ice_media_stream
*stream
,
389 std::vector
<NrIceCandidate
> *candidates
) {
390 MOZ_ASSERT(candidates
);
391 nr_ice_component
* comp
=STAILQ_FIRST(&stream
->components
);
393 if (comp
->state
!= NR_ICE_COMPONENT_DISABLED
) {
394 nr_ice_candidate
*cand
= TAILQ_FIRST(&comp
->candidates
);
396 NrIceCandidate new_cand
;
397 // This can fail if the candidate is server reflexive or relayed, and
398 // has not yet received a response (ie; it doesn't know its address
399 // yet). For the purposes of this code, this isn't a candidate we're
400 // interested in, since it is not fully baked yet.
401 if (ToNrIceCandidate(*cand
, &new_cand
)) {
402 candidates
->push_back(new_cand
);
404 cand
=TAILQ_NEXT(cand
,entry_comp
);
407 comp
=STAILQ_NEXT(comp
,entry
);
413 nsresult
NrIceMediaStream::GetLocalCandidates(
414 std::vector
<NrIceCandidate
>* candidates
) const {
415 return GetCandidatesFromStream(stream_
, candidates
);
418 nsresult
NrIceMediaStream::GetRemoteCandidates(
419 std::vector
<NrIceCandidate
>* candidates
) const {
420 nr_ice_media_stream
* peer_stream
;
421 int r
= nr_ice_peer_ctx_find_pstream(ctx_
->peer(), stream_
, &peer_stream
);
423 return NS_ERROR_FAILURE
;
426 return GetCandidatesFromStream(peer_stream
, candidates
);
430 nsresult
NrIceMediaStream::DisableComponent(int component_id
) {
432 return NS_ERROR_FAILURE
;
434 int r
= nr_ice_media_stream_disable_component(stream_
,
437 MOZ_MTLOG(ML_ERROR
, "Couldn't disable '" << name_
<< "':" <<
439 return NS_ERROR_FAILURE
;
445 nsresult
NrIceMediaStream::SendPacket(int component_id
,
446 const unsigned char *data
,
449 return NS_ERROR_FAILURE
;
451 int r
= nr_ice_media_stream_send(ctx_
->peer(), stream_
,
453 const_cast<unsigned char *>(data
), len
);
455 MOZ_MTLOG(ML_ERROR
, "Couldn't send media on '" << name_
<< "'");
456 if (r
== R_WOULDBLOCK
) {
457 return NS_BASE_STREAM_WOULD_BLOCK
;
460 return NS_BASE_STREAM_OSERROR
;
467 void NrIceMediaStream::Ready() {
468 // This function is called whenever a stream becomes ready, but it
469 // gets fired multiple times when a stream gets nominated repeatedly.
470 if (state_
!= ICE_OPEN
) {
471 MOZ_MTLOG(ML_DEBUG
, "Marking stream ready '" << name_
<< "'");
476 MOZ_MTLOG(ML_DEBUG
, "Stream ready callback fired again for '" << name_
<< "'");
480 void NrIceMediaStream::Close() {
481 MOZ_MTLOG(ML_DEBUG
, "Marking stream closed '" << name_
<< "'");