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