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 // Original author: ekr@rtfm.com
8
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
10
11 /*
12 Copyright (c) 2007, Adobe Systems, Incorporated
13 All rights reserved.
14
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
18
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
21
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.
25
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.
29
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.
41 */
42
43 #include <string>
44 #include <vector>
45
46 #include "logging.h"
47 #include "nsError.h"
48 #include "nsThreadUtils.h"
49
50 // nICEr includes
51 extern "C" {
52 #include "nr_api.h"
53 #include "registry.h"
54 #include "async_timer.h"
55 #include "ice_util.h"
56 #include "transport_addr.h"
57 #include "nr_crypto.h"
58 #include "nr_socket.h"
59 #include "nr_socket_local.h"
60 #include "stun_client_ctx.h"
61 #include "stun_server_ctx.h"
62 #include "ice_ctx.h"
63 #include "ice_candidate.h"
64 #include "ice_handler.h"
65 }
66
67 // Local includes
68 #include "nricectx.h"
69 #include "nricemediastream.h"
70
71 namespace mozilla {
72
73 MOZ_MTLOG_MODULE("mtransport")
74
ToNrIceAddr(nr_transport_addr & addr,NrIceAddr * out)75 static bool ToNrIceAddr(nr_transport_addr& addr, NrIceAddr* out) {
76 int r;
77 char addrstring[INET6_ADDRSTRLEN + 1];
78
79 r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
80 if (r) return false;
81 out->host = addrstring;
82
83 int port;
84 r = nr_transport_addr_get_port(&addr, &port);
85 if (r) return false;
86
87 out->port = port;
88
89 switch (addr.protocol) {
90 case IPPROTO_TCP:
91 if (addr.tls_host[0] != '\0') {
92 out->transport = kNrIceTransportTls;
93 } else {
94 out->transport = kNrIceTransportTcp;
95 }
96 break;
97 case IPPROTO_UDP:
98 out->transport = kNrIceTransportUdp;
99 break;
100 default:
101 MOZ_CRASH();
102 return false;
103 }
104
105 return true;
106 }
107
ToNrIceCandidate(const nr_ice_candidate & candc,NrIceCandidate * out)108 static bool ToNrIceCandidate(const nr_ice_candidate& candc,
109 NrIceCandidate* out) {
110 MOZ_ASSERT(out);
111 int r;
112 // Const-cast because the internal nICEr code isn't const-correct.
113 nr_ice_candidate* cand = const_cast<nr_ice_candidate*>(&candc);
114
115 if (!ToNrIceAddr(cand->addr, &out->cand_addr)) return false;
116
117 if (cand->mdns_addr) {
118 out->mdns_addr = cand->mdns_addr;
119 }
120
121 if (cand->isock) {
122 nr_transport_addr addr;
123 r = nr_socket_getaddr(cand->isock->sock, &addr);
124 if (r) return false;
125
126 out->is_proxied = addr.is_proxied;
127
128 if (!ToNrIceAddr(addr, &out->local_addr)) return false;
129 }
130
131 NrIceCandidate::Type type;
132
133 switch (cand->type) {
134 case HOST:
135 type = NrIceCandidate::ICE_HOST;
136 break;
137 case SERVER_REFLEXIVE:
138 type = NrIceCandidate::ICE_SERVER_REFLEXIVE;
139 break;
140 case PEER_REFLEXIVE:
141 type = NrIceCandidate::ICE_PEER_REFLEXIVE;
142 break;
143 case RELAYED:
144 type = NrIceCandidate::ICE_RELAYED;
145 break;
146 default:
147 return false;
148 }
149
150 NrIceCandidate::TcpType tcp_type;
151 switch (cand->tcp_type) {
152 case TCP_TYPE_ACTIVE:
153 tcp_type = NrIceCandidate::ICE_ACTIVE;
154 break;
155 case TCP_TYPE_PASSIVE:
156 tcp_type = NrIceCandidate::ICE_PASSIVE;
157 break;
158 case TCP_TYPE_SO:
159 tcp_type = NrIceCandidate::ICE_SO;
160 break;
161 default:
162 tcp_type = NrIceCandidate::ICE_NONE;
163 break;
164 }
165
166 out->type = type;
167 out->tcp_type = tcp_type;
168 out->codeword = candc.codeword;
169 out->label = candc.label;
170 out->trickled = candc.trickled;
171 out->priority = candc.priority;
172 return true;
173 }
174
175 // Make an NrIceCandidate from the candidate |cand|.
176 // This is not a member fxn because we want to hide the
177 // defn of nr_ice_candidate but we pass by reference.
MakeNrIceCandidate(const nr_ice_candidate & candc)178 static UniquePtr<NrIceCandidate> MakeNrIceCandidate(
179 const nr_ice_candidate& candc) {
180 UniquePtr<NrIceCandidate> out(new NrIceCandidate());
181
182 if (!ToNrIceCandidate(candc, out.get())) {
183 return nullptr;
184 }
185 return out;
186 }
187
Matches(const nr_ice_media_stream * stream,const std::string & ufrag,const std::string & pwd)188 static bool Matches(const nr_ice_media_stream* stream, const std::string& ufrag,
189 const std::string& pwd) {
190 return stream && (stream->ufrag == ufrag) && (stream->pwd == pwd);
191 }
192
NrIceMediaStream(NrIceCtx * ctx,const std::string & id,const std::string & name,size_t components)193 NrIceMediaStream::NrIceMediaStream(NrIceCtx* ctx, const std::string& id,
194 const std::string& name, size_t components)
195 : state_(ICE_CONNECTING),
196 ctx_(ctx->ctx()),
197 ctx_peer_(ctx->peer()),
198 name_(name),
199 components_(components),
200 stream_(nullptr),
201 old_stream_(nullptr),
202 id_(id) {}
203
~NrIceMediaStream()204 NrIceMediaStream::~NrIceMediaStream() {
205 // We do not need to destroy anything. All major resources
206 // are attached to the ice ctx.
207 }
208
ConnectToPeer(const std::string & ufrag,const std::string & pwd,const std::vector<std::string> & attributes)209 nsresult NrIceMediaStream::ConnectToPeer(
210 const std::string& ufrag, const std::string& pwd,
211 const std::vector<std::string>& attributes) {
212 MOZ_ASSERT(stream_);
213
214 if (Matches(old_stream_, ufrag, pwd)) {
215 // (We swap before we close so we never have stream_ == nullptr)
216 MOZ_MTLOG(ML_DEBUG,
217 "Rolling back to old stream ufrag=" << ufrag << " " << name_);
218 std::swap(stream_, old_stream_);
219 CloseStream(&old_stream_);
220 } else if (old_stream_) {
221 // Right now we wait for ICE to complete before closing the old stream.
222 // It might be worth it to close it sooner, but we don't want to close it
223 // right away.
224 MOZ_MTLOG(ML_DEBUG,
225 "ICE restart committed, marking old stream as obsolete, "
226 "beginning switchover to ufrag="
227 << ufrag << " " << name_);
228 nr_ice_media_stream_set_obsolete(old_stream_);
229 }
230
231 nr_ice_media_stream* peer_stream;
232 if (nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream)) {
233 // No peer yet
234 std::vector<char*> attributes_in;
235 attributes_in.reserve(attributes.size());
236 for (auto& attribute : attributes) {
237 MOZ_MTLOG(ML_DEBUG, "Setting " << attribute << " on stream " << name_);
238 attributes_in.push_back(const_cast<char*>(attribute.c_str()));
239 }
240
241 // Still need to call nr_ice_ctx_parse_stream_attributes.
242 int r = nr_ice_peer_ctx_parse_stream_attributes(
243 ctx_peer_, stream_, attributes_in.empty() ? nullptr : &attributes_in[0],
244 attributes_in.size());
245 if (r) {
246 MOZ_MTLOG(ML_ERROR,
247 "Couldn't parse attributes for stream " << name_ << "'");
248 return NS_ERROR_FAILURE;
249 }
250 }
251
252 return NS_OK;
253 }
254
SetIceCredentials(const std::string & ufrag,const std::string & pwd)255 nsresult NrIceMediaStream::SetIceCredentials(const std::string& ufrag,
256 const std::string& pwd) {
257 if (Matches(stream_, ufrag, pwd)) {
258 return NS_OK;
259 }
260
261 if (Matches(old_stream_, ufrag, pwd)) {
262 return NS_OK;
263 }
264
265 MOZ_MTLOG(ML_DEBUG, "Setting ICE credentials for " << name_ << " - " << ufrag
266 << ":" << pwd);
267 CloseStream(&old_stream_);
268 old_stream_ = stream_;
269
270 std::string name(name_ + " - " + ufrag + ":" + pwd);
271
272 int r = nr_ice_add_media_stream(ctx_, name.c_str(), ufrag.c_str(),
273 pwd.c_str(), components_, &stream_);
274 if (r) {
275 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
276 << name_ << "': error=" << r);
277 stream_ = old_stream_;
278 old_stream_ = nullptr;
279 return NS_ERROR_FAILURE;
280 }
281
282 state_ = ICE_CONNECTING;
283 return NS_OK;
284 }
285
286 // Parse trickle ICE candidate
ParseTrickleCandidate(const std::string & candidate,const std::string & ufrag,const std::string & mdns_addr)287 nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate,
288 const std::string& ufrag,
289 const std::string& mdns_addr) {
290 nr_ice_media_stream* stream = GetStreamForRemoteUfrag(ufrag);
291 if (!stream) {
292 return NS_ERROR_FAILURE;
293 }
294
295 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx_->label << ")/STREAM(" << name()
296 << ") : parsing trickle candidate "
297 << candidate);
298
299 int r = nr_ice_peer_ctx_parse_trickle_candidate(
300 ctx_peer_, stream, const_cast<char*>(candidate.c_str()),
301 mdns_addr.c_str());
302
303 if (r) {
304 if (r == R_ALREADY) {
305 MOZ_MTLOG(ML_INFO, "Trickle candidate is redundant for stream '"
306 << name_
307 << "' because it is completed: " << candidate);
308 } else if (r == R_REJECTED) {
309 MOZ_MTLOG(ML_INFO,
310 "Trickle candidate is ignored for stream '"
311 << name_
312 << "', probably because it is for an unused component"
313 << ": " << candidate);
314 } else {
315 MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
316 << name_ << "': " << candidate);
317 return NS_ERROR_FAILURE;
318 }
319 }
320
321 return NS_OK;
322 }
323
324 // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
GetActivePair(int component,UniquePtr<NrIceCandidate> * localp,UniquePtr<NrIceCandidate> * remotep)325 nsresult NrIceMediaStream::GetActivePair(int component,
326 UniquePtr<NrIceCandidate>* localp,
327 UniquePtr<NrIceCandidate>* remotep) {
328 int r;
329 nr_ice_candidate* local_int;
330 nr_ice_candidate* remote_int;
331
332 if (!stream_) {
333 return NS_ERROR_NOT_AVAILABLE;
334 }
335
336 r = nr_ice_media_stream_get_active(ctx_peer_, stream_, component, &local_int,
337 &remote_int);
338 // If result is R_REJECTED then component is unpaired or disabled.
339 if (r == R_REJECTED) return NS_ERROR_NOT_AVAILABLE;
340
341 if (r) return NS_ERROR_FAILURE;
342
343 UniquePtr<NrIceCandidate> local(MakeNrIceCandidate(*local_int));
344 if (!local) return NS_ERROR_FAILURE;
345
346 UniquePtr<NrIceCandidate> remote(MakeNrIceCandidate(*remote_int));
347 if (!remote) return NS_ERROR_FAILURE;
348
349 if (localp) *localp = std::move(local);
350 if (remotep) *remotep = std::move(remote);
351
352 return NS_OK;
353 }
354
GetCandidatePairs(std::vector<NrIceCandidatePair> * out_pairs) const355 nsresult NrIceMediaStream::GetCandidatePairs(
356 std::vector<NrIceCandidatePair>* out_pairs) const {
357 MOZ_ASSERT(out_pairs);
358 if (!stream_) {
359 return NS_ERROR_NOT_AVAILABLE;
360 }
361
362 // If we haven't at least started checking then there is nothing to report
363 if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
364 return NS_OK;
365 }
366
367 // Get the check_list on the peer stream (this is where the check_list
368 // actually lives, not in stream_)
369 nr_ice_media_stream* peer_stream;
370 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
371 if (r != 0) {
372 return NS_ERROR_FAILURE;
373 }
374
375 nr_ice_cand_pair *p1, *p2;
376 out_pairs->clear();
377
378 TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) {
379 MOZ_ASSERT(p1);
380 MOZ_ASSERT(p1->local);
381 MOZ_ASSERT(p1->remote);
382 NrIceCandidatePair pair;
383
384 p2 = TAILQ_FIRST(&peer_stream->check_list);
385 while (p2) {
386 if (p1 == p2) {
387 /* Don't compare with our self. */
388 p2 = TAILQ_NEXT(p2, check_queue_entry);
389 continue;
390 }
391 if (strncmp(p1->codeword, p2->codeword, sizeof(p1->codeword)) == 0) {
392 /* In case of duplicate pairs we only report the one winning pair */
393 if (((p2->remote->component && (p2->remote->component->active == p2)) &&
394 !(p1->remote->component &&
395 (p1->remote->component->active == p1))) ||
396 ((p2->peer_nominated || p2->nominated) &&
397 !(p1->peer_nominated || p1->nominated)) ||
398 (p2->priority > p1->priority) ||
399 ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) &&
400 (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) ||
401 ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) &&
402 (p1->state == NR_ICE_PAIR_STATE_CANCELLED))) {
403 /* p2 is a better pair. */
404 break;
405 }
406 }
407 p2 = TAILQ_NEXT(p2, check_queue_entry);
408 }
409 if (p2) {
410 /* p2 points to a duplicate but better pair so skip this one */
411 continue;
412 }
413
414 switch (p1->state) {
415 case NR_ICE_PAIR_STATE_FROZEN:
416 pair.state = NrIceCandidatePair::State::STATE_FROZEN;
417 break;
418 case NR_ICE_PAIR_STATE_WAITING:
419 pair.state = NrIceCandidatePair::State::STATE_WAITING;
420 break;
421 case NR_ICE_PAIR_STATE_IN_PROGRESS:
422 pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS;
423 break;
424 case NR_ICE_PAIR_STATE_FAILED:
425 pair.state = NrIceCandidatePair::State::STATE_FAILED;
426 break;
427 case NR_ICE_PAIR_STATE_SUCCEEDED:
428 pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED;
429 break;
430 case NR_ICE_PAIR_STATE_CANCELLED:
431 pair.state = NrIceCandidatePair::State::STATE_CANCELLED;
432 break;
433 default:
434 MOZ_ASSERT(0);
435 }
436
437 pair.priority = p1->priority;
438 pair.nominated = p1->peer_nominated || p1->nominated;
439 pair.component_id = p1->remote->component->component_id;
440
441 // As discussed with drno: a component's can_send field (set to true
442 // by ICE consent) is a very close approximation for writable and
443 // readable. Note: the component for the local candidate never has
444 // the can_send member set to true, remote for both readable and
445 // writable. (mjf)
446 pair.writable = p1->remote->component->can_send;
447 pair.readable = p1->remote->component->can_send;
448 pair.selected =
449 p1->remote->component && p1->remote->component->active == p1;
450 pair.codeword = p1->codeword;
451 pair.bytes_sent = p1->bytes_sent;
452 pair.bytes_recvd = p1->bytes_recvd;
453 pair.ms_since_last_send =
454 p1->last_sent.tv_sec * 1000 + p1->last_sent.tv_usec / 1000;
455 pair.ms_since_last_recv =
456 p1->last_recvd.tv_sec * 1000 + p1->last_recvd.tv_usec / 1000;
457
458 if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
459 !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
460 return NS_ERROR_FAILURE;
461 }
462
463 out_pairs->push_back(pair);
464 }
465
466 return NS_OK;
467 }
468
GetDefaultCandidate(int component,NrIceCandidate * candidate) const469 nsresult NrIceMediaStream::GetDefaultCandidate(
470 int component, NrIceCandidate* candidate) const {
471 if (!stream_) {
472 return NS_ERROR_NOT_AVAILABLE;
473 }
474
475 nr_ice_candidate* cand;
476
477 int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
478 if (r) {
479 if (r == R_NOT_FOUND) {
480 MOZ_MTLOG(ML_INFO, "Couldn't get default ICE candidate for '"
481 << name_ << "', no candidates.");
482 } else {
483 MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
484 << name_ << "', " << r);
485 }
486 return NS_ERROR_FAILURE;
487 }
488
489 if (!ToNrIceCandidate(*cand, candidate)) {
490 MOZ_MTLOG(ML_ERROR,
491 "Failed to convert default ICE candidate for '" << name_ << "'");
492 return NS_ERROR_FAILURE;
493 }
494
495 return NS_OK;
496 }
497
GetAttributes() const498 std::vector<std::string> NrIceMediaStream::GetAttributes() const {
499 char** attrs = nullptr;
500 int attrct;
501 int r;
502 std::vector<std::string> ret;
503
504 if (!stream_) {
505 return ret;
506 }
507
508 r = nr_ice_media_stream_get_attributes(stream_, &attrs, &attrct);
509 if (r) {
510 MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" << name_ << "'");
511 return ret;
512 }
513
514 for (int i = 0; i < attrct; i++) {
515 ret.push_back(attrs[i]);
516 RFREE(attrs[i]);
517 }
518
519 RFREE(attrs);
520
521 return ret;
522 }
523
GetCandidatesFromStream(nr_ice_media_stream * stream,std::vector<NrIceCandidate> * candidates)524 static nsresult GetCandidatesFromStream(
525 nr_ice_media_stream* stream, std::vector<NrIceCandidate>* candidates) {
526 MOZ_ASSERT(candidates);
527 nr_ice_component* comp = STAILQ_FIRST(&stream->components);
528 while (comp) {
529 if (comp->state != NR_ICE_COMPONENT_DISABLED) {
530 nr_ice_candidate* cand = TAILQ_FIRST(&comp->candidates);
531 while (cand) {
532 NrIceCandidate new_cand;
533 // This can fail if the candidate is server reflexive or relayed, and
534 // has not yet received a response (ie; it doesn't know its address
535 // yet). For the purposes of this code, this isn't a candidate we're
536 // interested in, since it is not fully baked yet.
537 if (ToNrIceCandidate(*cand, &new_cand)) {
538 candidates->push_back(new_cand);
539 }
540 cand = TAILQ_NEXT(cand, entry_comp);
541 }
542 }
543 comp = STAILQ_NEXT(comp, entry);
544 }
545
546 return NS_OK;
547 }
548
GetLocalCandidates(std::vector<NrIceCandidate> * candidates) const549 nsresult NrIceMediaStream::GetLocalCandidates(
550 std::vector<NrIceCandidate>* candidates) const {
551 if (!stream_) {
552 return NS_ERROR_NOT_AVAILABLE;
553 }
554
555 return GetCandidatesFromStream(stream_, candidates);
556 }
557
GetRemoteCandidates(std::vector<NrIceCandidate> * candidates) const558 nsresult NrIceMediaStream::GetRemoteCandidates(
559 std::vector<NrIceCandidate>* candidates) const {
560 if (!stream_) {
561 return NS_ERROR_NOT_AVAILABLE;
562 }
563
564 // If we haven't at least started checking then there is nothing to report
565 if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
566 return NS_OK;
567 }
568
569 nr_ice_media_stream* peer_stream;
570 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
571 if (r != 0) {
572 return NS_ERROR_FAILURE;
573 }
574
575 return GetCandidatesFromStream(peer_stream, candidates);
576 }
577
DisableComponent(int component_id)578 nsresult NrIceMediaStream::DisableComponent(int component_id) {
579 if (!stream_) return NS_ERROR_FAILURE;
580
581 int r = nr_ice_media_stream_disable_component(stream_, component_id);
582 if (r) {
583 MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << component_id);
584 return NS_ERROR_FAILURE;
585 }
586
587 return NS_OK;
588 }
589
GetConsentStatus(int component_id,bool * can_send,struct timeval * ts)590 nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool* can_send,
591 struct timeval* ts) {
592 if (!stream_) return NS_ERROR_FAILURE;
593
594 nr_ice_media_stream* peer_stream;
595 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
596 if (r) {
597 MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '"
598 << name_ << "':" << component_id);
599 return NS_ERROR_FAILURE;
600 }
601
602 int send = 0;
603 r = nr_ice_media_stream_get_consent_status(peer_stream, component_id, &send,
604 ts);
605 if (r) {
606 MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '"
607 << name_ << "':" << component_id);
608 return NS_ERROR_FAILURE;
609 }
610 *can_send = !!send;
611
612 return NS_OK;
613 }
614
HasStream(nr_ice_media_stream * stream) const615 bool NrIceMediaStream::HasStream(nr_ice_media_stream* stream) const {
616 return (stream == stream_) || (stream == old_stream_);
617 }
618
SendPacket(int component_id,const unsigned char * data,size_t len)619 nsresult NrIceMediaStream::SendPacket(int component_id,
620 const unsigned char* data, size_t len) {
621 nr_ice_media_stream* stream = old_stream_ ? old_stream_ : stream_;
622 if (!stream) {
623 return NS_ERROR_FAILURE;
624 }
625
626 int r = nr_ice_media_stream_send(ctx_peer_, stream, component_id,
627 const_cast<unsigned char*>(data), len);
628 if (r) {
629 MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'");
630 if (r == R_WOULDBLOCK) {
631 return NS_BASE_STREAM_WOULD_BLOCK;
632 }
633
634 return NS_BASE_STREAM_OSERROR;
635 }
636
637 return NS_OK;
638 }
639
Ready()640 void NrIceMediaStream::Ready() {
641 // This function is called whenever a stream becomes ready, but it
642 // gets fired multiple times when a stream gets nominated repeatedly.
643 if (state_ != ICE_OPEN) {
644 MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'");
645 state_ = ICE_OPEN;
646 NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>(
647 "NrIceMediaStream::DeferredCloseOldStream", this,
648 &NrIceMediaStream::DeferredCloseOldStream, old_stream_));
649 SignalReady(this);
650 } else {
651 MOZ_MTLOG(ML_DEBUG,
652 "Stream ready callback fired again for '" << name_ << "'");
653 }
654 }
655
Failed()656 void NrIceMediaStream::Failed() {
657 if (state_ != ICE_CLOSED) {
658 MOZ_MTLOG(ML_DEBUG, "Marking stream failed '" << name_ << "'");
659 state_ = ICE_CLOSED;
660 // We don't need the old stream anymore.
661 NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>(
662 "NrIceMediaStream::DeferredCloseOldStream", this,
663 &NrIceMediaStream::DeferredCloseOldStream, old_stream_));
664 SignalFailed(this);
665 }
666 }
667
Close()668 void NrIceMediaStream::Close() {
669 MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
670 state_ = ICE_CLOSED;
671
672 CloseStream(&old_stream_);
673 CloseStream(&stream_);
674 }
675
CloseStream(nr_ice_media_stream ** stream)676 void NrIceMediaStream::CloseStream(nr_ice_media_stream** stream) {
677 if (*stream) {
678 int r = nr_ice_remove_media_stream(ctx_, stream);
679 if (r) {
680 MOZ_ASSERT(false, "Failed to remove stream");
681 MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
682 }
683 *stream = nullptr;
684 }
685 }
686
DeferredCloseOldStream(const nr_ice_media_stream * old)687 void NrIceMediaStream::DeferredCloseOldStream(const nr_ice_media_stream* old) {
688 if (old == old_stream_) {
689 CloseStream(&old_stream_);
690 }
691 }
692
GetStreamForRemoteUfrag(const std::string & aUfrag)693 nr_ice_media_stream* NrIceMediaStream::GetStreamForRemoteUfrag(
694 const std::string& aUfrag) {
695 if (aUfrag.empty()) {
696 return stream_;
697 }
698
699 nr_ice_media_stream* peer_stream = nullptr;
700
701 if (!nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream) &&
702 aUfrag == peer_stream->ufrag) {
703 return stream_;
704 }
705
706 if (old_stream_ &&
707 !nr_ice_peer_ctx_find_pstream(ctx_peer_, old_stream_, &peer_stream) &&
708 aUfrag == peer_stream->ufrag) {
709 return old_stream_;
710 }
711
712 return nullptr;
713 }
714
715 } // namespace mozilla
716