tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nricemediastream.cpp (24102B)


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