tor-browser

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

SdpHelper.cpp (27612B)


      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 #include "sdp/SdpHelper.h"
      8 
      9 #include <string.h>
     10 
     11 #include <set>
     12 
     13 #include "nsDebug.h"
     14 #include "nsError.h"
     15 #include "prprf.h"
     16 #include "sdp/Sdp.h"
     17 #include "sdp/SdpMediaSection.h"
     18 #include "transport/logging.h"
     19 
     20 namespace mozilla {
     21 MOZ_MTLOG_MODULE("sdp")
     22 
     23 #define SDP_SET_ERROR(error)         \
     24  do {                               \
     25    std::ostringstream os;           \
     26    os << error;                     \
     27    mLastError = os.str();           \
     28    MOZ_MTLOG(ML_ERROR, mLastError); \
     29  } while (0);
     30 
     31 nsresult SdpHelper::CopyTransportParams(size_t numComponents,
     32                                        const SdpMediaSection& oldLocal,
     33                                        SdpMediaSection* newLocal) {
     34  const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList();
     35  // Copy over m-section details
     36  if (!oldLocalAttrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) {
     37    // Do not copy port 0 from an offer with a=bundle-only; this could cause
     38    // an answer msection to be erroneously rejected.
     39    newLocal->SetPort(oldLocal.GetPort());
     40  }
     41  newLocal->GetConnection() = oldLocal.GetConnection();
     42 
     43  SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList();
     44 
     45  // Now we copy over attributes that won't be added by the usual logic
     46  if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) &&
     47      numComponents) {
     48    UniquePtr<SdpMultiStringAttribute> candidateAttrs(
     49        new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
     50    for (const std::string& candidate : oldLocalAttrs.GetCandidate()) {
     51      size_t component;
     52      nsresult rv = GetComponent(candidate, &component);
     53      NS_ENSURE_SUCCESS(rv, rv);
     54      if (numComponents >= component) {
     55        candidateAttrs->mValues.push_back(candidate);
     56      }
     57    }
     58    if (!candidateAttrs->mValues.empty()) {
     59      newLocalAttrs.SetAttribute(candidateAttrs.release());
     60    }
     61  }
     62 
     63  if (oldLocalAttrs.HasAttribute(SdpAttribute::kEndOfCandidatesAttribute)) {
     64    newLocalAttrs.SetAttribute(
     65        new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
     66  }
     67 
     68  if (numComponents == 2 &&
     69      oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
     70    // copy rtcp attribute if we had one that we are using
     71    newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
     72  }
     73 
     74  return NS_OK;
     75 }
     76 
     77 bool SdpHelper::AreOldTransportParamsValid(const Sdp& oldAnswer,
     78                                           const Sdp& offerersPreviousSdp,
     79                                           const Sdp& newOffer, size_t level) {
     80  if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) ||
     81      MsectionIsDisabled(newOffer.GetMediaSection(level))) {
     82    // Obvious
     83    return false;
     84  }
     85 
     86  if (!OwnsTransport(oldAnswer, level, sdp::kAnswer)) {
     87    // The transport attributes on this m-section were thrown away, because it
     88    // was bundled.
     89    return false;
     90  }
     91 
     92  if (!OwnsTransport(newOffer, level, sdp::kOffer)) {
     93    return false;
     94  }
     95 
     96  if (IceCredentialsDiffer(newOffer.GetMediaSection(level),
     97                           offerersPreviousSdp.GetMediaSection(level))) {
     98    return false;
     99  }
    100 
    101  return true;
    102 }
    103 
    104 bool SdpHelper::IceCredentialsDiffer(const SdpMediaSection& msection1,
    105                                     const SdpMediaSection& msection2) {
    106  const SdpAttributeList& attrs1(msection1.GetAttributeList());
    107  const SdpAttributeList& attrs2(msection2.GetAttributeList());
    108 
    109  if ((attrs1.GetIceUfrag() != attrs2.GetIceUfrag()) ||
    110      (attrs1.GetIcePwd() != attrs2.GetIcePwd())) {
    111    return true;
    112  }
    113 
    114  return false;
    115 }
    116 
    117 nsresult SdpHelper::GetComponent(const std::string& candidate,
    118                                 size_t* component) {
    119  unsigned int temp;
    120  int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp);
    121  if (result == 1) {
    122    *component = temp;
    123    return NS_OK;
    124  }
    125  SDP_SET_ERROR("Malformed ICE candidate: " << candidate);
    126  return NS_ERROR_INVALID_ARG;
    127 }
    128 
    129 bool SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const {
    130  return !msection.GetPort() && !msection.GetAttributeList().HasAttribute(
    131                                    SdpAttribute::kBundleOnlyAttribute);
    132 }
    133 
    134 void SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection) {
    135  std::string mid;
    136 
    137  // Make sure to remove the mid from any group attributes
    138  if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
    139    mid = msection->GetAttributeList().GetMid();
    140    if (sdp->GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
    141      UniquePtr<SdpGroupAttributeList> newGroupAttr(
    142          new SdpGroupAttributeList(sdp->GetAttributeList().GetGroup()));
    143      newGroupAttr->RemoveMid(mid);
    144      sdp->GetAttributeList().SetAttribute(newGroupAttr.release());
    145    }
    146  }
    147 
    148  // Clear out attributes.
    149  msection->GetAttributeList().Clear();
    150 
    151  auto* direction = new SdpDirectionAttribute(SdpDirectionAttribute::kInactive);
    152  msection->GetAttributeList().SetAttribute(direction);
    153  msection->SetPort(0);
    154 
    155  // maintain the mid for easier identification on other side
    156  if (!mid.empty()) {
    157    msection->GetAttributeList().SetAttribute(
    158        new SdpStringAttribute(SdpAttribute::kMidAttribute, mid));
    159  }
    160 
    161  msection->ClearCodecs();
    162 
    163  auto mediaType = msection->GetMediaType();
    164  switch (mediaType) {
    165    case SdpMediaSection::kAudio:
    166      msection->AddCodec("0", "PCMU", 8000, 1);
    167      break;
    168    case SdpMediaSection::kVideo:
    169      msection->AddCodec("120", "VP8", 90000, 1);
    170      break;
    171    case SdpMediaSection::kApplication:
    172      msection->AddDataChannel("webrtc-datachannel", 0, 0, 0);
    173      break;
    174    default:
    175      // We need to have something here to fit the grammar, this seems safe
    176      // and 19 is a reserved payload type which should not be used by anyone.
    177      msection->AddCodec("19", "reserved", 8000, 1);
    178  }
    179 }
    180 
    181 void SdpHelper::GetBundleGroups(
    182    const Sdp& sdp,
    183    std::vector<SdpGroupAttributeList::Group>* bundleGroups) const {
    184  if (sdp.GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
    185    for (auto& group : sdp.GetAttributeList().GetGroup().mGroups) {
    186      if (group.semantics == SdpGroupAttributeList::kBundle) {
    187        bundleGroups->push_back(group);
    188      }
    189    }
    190  }
    191 }
    192 
    193 nsresult SdpHelper::GetBundledMids(const Sdp& sdp, BundledMids* bundledMids) {
    194  std::vector<SdpGroupAttributeList::Group> bundleGroups;
    195  GetBundleGroups(sdp, &bundleGroups);
    196 
    197  for (SdpGroupAttributeList::Group& group : bundleGroups) {
    198    if (group.tags.empty()) {
    199      continue;
    200    }
    201 
    202    const SdpMediaSection* msection(FindMsectionByMid(sdp, group.tags[0]));
    203 
    204    if (!msection) {
    205      SDP_SET_ERROR(
    206          "mid specified for bundle transport in group attribute"
    207          " does not exist in the SDP. (mid="
    208          << group.tags[0] << ")");
    209      return NS_ERROR_INVALID_ARG;
    210    }
    211 
    212    if (MsectionIsDisabled(*msection)) {
    213      SDP_SET_ERROR(
    214          "mid specified for bundle transport in group attribute"
    215          " points at a disabled m-section. (mid="
    216          << group.tags[0] << ")");
    217      return NS_ERROR_INVALID_ARG;
    218    }
    219 
    220    for (const std::string& mid : group.tags) {
    221      if (bundledMids->count(mid)) {
    222        SDP_SET_ERROR("mid \'" << mid
    223                               << "\' appears more than once in a "
    224                                  "BUNDLE group");
    225        return NS_ERROR_INVALID_ARG;
    226      }
    227 
    228      (*bundledMids)[mid] = msection;
    229    }
    230  }
    231 
    232  return NS_OK;
    233 }
    234 
    235 bool SdpHelper::OwnsTransport(const Sdp& sdp, uint16_t level,
    236                              sdp::SdpType type) {
    237  auto& msection = sdp.GetMediaSection(level);
    238 
    239  BundledMids bundledMids;
    240  nsresult rv = GetBundledMids(sdp, &bundledMids);
    241  if (NS_FAILED(rv)) {
    242    // Should have been caught sooner.
    243    MOZ_ASSERT(false);
    244    return true;
    245  }
    246 
    247  return OwnsTransport(msection, bundledMids, type);
    248 }
    249 
    250 bool SdpHelper::OwnsTransport(const SdpMediaSection& msection,
    251                              const BundledMids& bundledMids,
    252                              sdp::SdpType type) {
    253  if (MsectionIsDisabled(msection)) {
    254    return false;
    255  }
    256 
    257  if (!msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
    258    // No mid, definitely no bundle for this m-section
    259    return true;
    260  }
    261  std::string mid(msection.GetAttributeList().GetMid());
    262  if (type != sdp::kOffer || msection.GetAttributeList().HasAttribute(
    263                                 SdpAttribute::kBundleOnlyAttribute)) {
    264    // If this is an answer, or this m-section is marked bundle-only, the group
    265    // attribute is authoritative. Otherwise, we aren't sure.
    266    if (bundledMids.count(mid) && &msection != bundledMids.at(mid)) {
    267      // mid is bundled, and isn't the bundle m-section
    268      return false;
    269    }
    270  }
    271 
    272  return true;
    273 }
    274 
    275 nsresult SdpHelper::GetMidFromLevel(const Sdp& sdp, uint16_t level,
    276                                    std::string* mid) {
    277  if (level >= sdp.GetMediaSectionCount()) {
    278    SDP_SET_ERROR("Index " << level << " out of range");
    279    return NS_ERROR_INVALID_ARG;
    280  }
    281 
    282  const SdpMediaSection& msection = sdp.GetMediaSection(level);
    283  const SdpAttributeList& attrList = msection.GetAttributeList();
    284 
    285  // grab the mid and set the outparam
    286  if (attrList.HasAttribute(SdpAttribute::kMidAttribute)) {
    287    *mid = attrList.GetMid();
    288  }
    289 
    290  return NS_OK;
    291 }
    292 
    293 nsresult SdpHelper::AddCandidateToSdp(Sdp* sdp,
    294                                      const std::string& candidateUntrimmed,
    295                                      uint16_t level,
    296                                      const std::string& ufrag) {
    297  if (level >= sdp->GetMediaSectionCount()) {
    298    SDP_SET_ERROR("Index " << level << " out of range");
    299    return NS_ERROR_INVALID_ARG;
    300  }
    301 
    302  SdpMediaSection& msection = sdp->GetMediaSection(level);
    303  SdpAttributeList& attrList = msection.GetAttributeList();
    304 
    305  if (!ufrag.empty()) {
    306    if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) ||
    307        attrList.GetIceUfrag() != ufrag) {
    308      SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")");
    309      return NS_ERROR_INVALID_ARG;
    310    }
    311  }
    312 
    313  if (candidateUntrimmed.empty()) {
    314    SetIceGatheringComplete(sdp, level, ufrag);
    315    return NS_OK;
    316  }
    317 
    318  // Trim off '[a=]candidate:'
    319  size_t begin = candidateUntrimmed.find(':');
    320  if (begin == std::string::npos) {
    321    SDP_SET_ERROR("Invalid candidate, no ':' (" << candidateUntrimmed << ")");
    322    return NS_ERROR_INVALID_ARG;
    323  }
    324  ++begin;
    325 
    326  std::string candidate = candidateUntrimmed.substr(begin);
    327 
    328  UniquePtr<SdpMultiStringAttribute> candidates;
    329  if (!attrList.HasAttribute(SdpAttribute::kCandidateAttribute)) {
    330    // Create new
    331    candidates.reset(
    332        new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
    333  } else {
    334    // Copy existing
    335    candidates.reset(new SdpMultiStringAttribute(
    336        *static_cast<const SdpMultiStringAttribute*>(
    337            attrList.GetAttribute(SdpAttribute::kCandidateAttribute))));
    338  }
    339  candidates->PushEntry(candidate);
    340  attrList.SetAttribute(candidates.release());
    341 
    342  return NS_OK;
    343 }
    344 
    345 nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp,
    346                                            const std::string& ufrag) {
    347  for (uint16_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
    348    nsresult rv = SetIceGatheringComplete(sdp, i, ufrag);
    349    NS_ENSURE_SUCCESS(rv, rv);
    350  }
    351  return NS_OK;
    352 }
    353 
    354 nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp, uint16_t level,
    355                                            const std::string& ufrag) {
    356  if (level >= sdp->GetMediaSectionCount()) {
    357    SDP_SET_ERROR("Index " << level << " out of range");
    358    return NS_ERROR_INVALID_ARG;
    359  }
    360 
    361  SdpMediaSection& msection = sdp->GetMediaSection(level);
    362  SdpAttributeList& attrList = msection.GetAttributeList();
    363 
    364  if (!ufrag.empty()) {
    365    if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) ||
    366        attrList.GetIceUfrag() != ufrag) {
    367      SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")");
    368      return NS_ERROR_INVALID_ARG;
    369    }
    370  }
    371 
    372  attrList.SetAttribute(
    373      new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
    374  // Remove trickle-ice option
    375  attrList.RemoveAttribute(SdpAttribute::kIceOptionsAttribute);
    376  return NS_OK;
    377 }
    378 
    379 void SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr,
    380                                    uint16_t defaultCandidatePort,
    381                                    const std::string& defaultRtcpCandidateAddr,
    382                                    uint16_t defaultRtcpCandidatePort,
    383                                    SdpMediaSection* msection) {
    384  SdpAttributeList& attrList = msection->GetAttributeList();
    385 
    386  msection->GetConnection().SetAddress(defaultCandidateAddr);
    387  msection->SetPort(defaultCandidatePort);
    388  if (!defaultRtcpCandidateAddr.empty()) {
    389    sdp::AddrType ipVersion = sdp::kIPv4;
    390    if (defaultRtcpCandidateAddr.find(':') != std::string::npos) {
    391      ipVersion = sdp::kIPv6;
    392    }
    393    attrList.SetAttribute(new SdpRtcpAttribute(defaultRtcpCandidatePort,
    394                                               sdp::kInternet, ipVersion,
    395                                               defaultRtcpCandidateAddr));
    396  }
    397 }
    398 
    399 nsresult SdpHelper::GetIdsFromMsid(const Sdp& sdp,
    400                                   const SdpMediaSection& msection,
    401                                   std::vector<std::string>* streamIds) {
    402  std::vector<SdpMsidAttributeList::Msid> allMsids;
    403  nsresult rv = GetMsids(msection, &allMsids);
    404  NS_ENSURE_SUCCESS(rv, rv);
    405 
    406  if (allMsids.empty()) {
    407    return NS_ERROR_NOT_AVAILABLE;
    408  }
    409 
    410  streamIds->clear();
    411  for (const auto& msid : allMsids) {
    412    // "-" means no stream, see draft-ietf-mmusic-msid
    413    // Remove duplicates, but leave order the same
    414    if (msid.identifier != "-" &&
    415        !std::count(streamIds->begin(), streamIds->end(), msid.identifier)) {
    416      streamIds->push_back(msid.identifier);
    417    }
    418  }
    419 
    420  return NS_OK;
    421 }
    422 
    423 nsresult SdpHelper::GetMsids(const SdpMediaSection& msection,
    424                             std::vector<SdpMsidAttributeList::Msid>* msids) {
    425  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
    426    *msids = msection.GetAttributeList().GetMsid().mMsids;
    427    return NS_OK;
    428  }
    429 
    430  // If there are no a=msid, can we find msids in ssrc attributes?
    431  // (Chrome does not put plain-old msid attributes in its SDP)
    432  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
    433    auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
    434 
    435    for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
    436      if (i->attribute.starts_with("msid:")) {
    437        std::string streamId;
    438        std::string trackId;
    439        nsresult rv = ParseMsid(i->attribute, &streamId, &trackId);
    440        NS_ENSURE_SUCCESS(rv, rv);
    441        msids->push_back({streamId, trackId});
    442      }
    443    }
    444  }
    445 
    446  return NS_OK;
    447 }
    448 
    449 nsresult SdpHelper::ParseMsid(const std::string& msidAttribute,
    450                              std::string* streamId, std::string* trackId) {
    451  // Would be nice if SdpSsrcAttributeList could parse out the contained
    452  // attribute, but at least the parse here is simple.
    453  // We are being very forgiving here wrt whitespace; tabs are not actually
    454  // allowed, nor is leading/trailing whitespace.
    455  size_t streamIdStart = msidAttribute.find_first_not_of(" \t", 5);
    456  // We do not assume the appdata token is here, since this is not
    457  // necessarily a webrtc msid
    458  if (streamIdStart == std::string::npos) {
    459    SDP_SET_ERROR("Malformed source-level msid attribute: " << msidAttribute);
    460    return NS_ERROR_INVALID_ARG;
    461  }
    462 
    463  size_t streamIdEnd = msidAttribute.find_first_of(" \t", streamIdStart);
    464  if (streamIdEnd == std::string::npos) {
    465    streamIdEnd = msidAttribute.size();
    466  }
    467 
    468  size_t trackIdStart = msidAttribute.find_first_not_of(" \t", streamIdEnd);
    469  if (trackIdStart == std::string::npos) {
    470    trackIdStart = msidAttribute.size();
    471  }
    472 
    473  size_t trackIdEnd = msidAttribute.find_first_of(" \t", trackIdStart);
    474  if (trackIdEnd == std::string::npos) {
    475    trackIdEnd = msidAttribute.size();
    476  }
    477 
    478  size_t streamIdSize = streamIdEnd - streamIdStart;
    479  size_t trackIdSize = trackIdEnd - trackIdStart;
    480 
    481  *streamId = msidAttribute.substr(streamIdStart, streamIdSize);
    482  *trackId = msidAttribute.substr(trackIdStart, trackIdSize);
    483  return NS_OK;
    484 }
    485 
    486 void SdpHelper::SetupMsidSemantic(const std::vector<std::string>& msids,
    487                                  Sdp* sdp) const {
    488  if (!msids.empty()) {
    489    UniquePtr<SdpMsidSemanticAttributeList> msidSemantics(
    490        new SdpMsidSemanticAttributeList);
    491    msidSemantics->PushEntry("WMS", msids);
    492    sdp->GetAttributeList().SetAttribute(msidSemantics.release());
    493  }
    494 }
    495 
    496 std::string SdpHelper::GetCNAME(const SdpMediaSection& msection) const {
    497  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
    498    auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
    499    for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
    500      if (i->attribute.starts_with("cname:")) {
    501        return i->attribute.substr(6);
    502      }
    503    }
    504  }
    505  return "";
    506 }
    507 
    508 const SdpMediaSection* SdpHelper::FindMsectionByMid(
    509    const Sdp& sdp, const std::string& mid) const {
    510  for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
    511    auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
    512    if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
    513        attrs.GetMid() == mid) {
    514      return &sdp.GetMediaSection(i);
    515    }
    516  }
    517  return nullptr;
    518 }
    519 
    520 SdpMediaSection* SdpHelper::FindMsectionByMid(Sdp& sdp,
    521                                              const std::string& mid) const {
    522  for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
    523    auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
    524    if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
    525        attrs.GetMid() == mid) {
    526      return &sdp.GetMediaSection(i);
    527    }
    528  }
    529  return nullptr;
    530 }
    531 
    532 nsresult SdpHelper::CopyStickyParams(const SdpMediaSection& source,
    533                                     SdpMediaSection* dest) {
    534  auto& sourceAttrs = source.GetAttributeList();
    535  auto& destAttrs = dest->GetAttributeList();
    536 
    537  // There's no reason to renegotiate rtcp-mux
    538  if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
    539    destAttrs.SetAttribute(
    540        new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
    541  }
    542 
    543  // mid should stay the same
    544  if (sourceAttrs.HasAttribute(SdpAttribute::kMidAttribute)) {
    545    destAttrs.SetAttribute(new SdpStringAttribute(SdpAttribute::kMidAttribute,
    546                                                  sourceAttrs.GetMid()));
    547  }
    548 
    549  // Keep RTCP mode setting
    550  if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpRsizeAttribute) &&
    551      source.GetMediaType() == SdpMediaSection::kVideo) {
    552    destAttrs.SetAttribute(
    553        new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
    554  }
    555 
    556  // Keep extmap-allow-mixed setting
    557  if (sourceAttrs.HasAttribute(SdpAttribute::kExtmapAllowMixedAttribute)) {
    558    destAttrs.SetAttribute(
    559        new SdpFlagAttribute(SdpAttribute::kExtmapAllowMixedAttribute));
    560  }
    561  return NS_OK;
    562 }
    563 
    564 bool SdpHelper::HasRtcp(SdpMediaSection::Protocol proto) const {
    565  switch (proto) {
    566    case SdpMediaSection::kRtpAvpf:
    567    case SdpMediaSection::kDccpRtpAvpf:
    568    case SdpMediaSection::kDccpRtpSavpf:
    569    case SdpMediaSection::kRtpSavpf:
    570    case SdpMediaSection::kUdpTlsRtpSavpf:
    571    case SdpMediaSection::kTcpDtlsRtpSavpf:
    572    case SdpMediaSection::kDccpTlsRtpSavpf:
    573      return true;
    574    case SdpMediaSection::kRtpAvp:
    575    case SdpMediaSection::kUdp:
    576    case SdpMediaSection::kVat:
    577    case SdpMediaSection::kRtp:
    578    case SdpMediaSection::kUdptl:
    579    case SdpMediaSection::kTcp:
    580    case SdpMediaSection::kTcpRtpAvp:
    581    case SdpMediaSection::kRtpSavp:
    582    case SdpMediaSection::kTcpBfcp:
    583    case SdpMediaSection::kTcpTlsBfcp:
    584    case SdpMediaSection::kTcpTls:
    585    case SdpMediaSection::kFluteUdp:
    586    case SdpMediaSection::kTcpMsrp:
    587    case SdpMediaSection::kTcpTlsMsrp:
    588    case SdpMediaSection::kDccp:
    589    case SdpMediaSection::kDccpRtpAvp:
    590    case SdpMediaSection::kDccpRtpSavp:
    591    case SdpMediaSection::kUdpTlsRtpSavp:
    592    case SdpMediaSection::kTcpDtlsRtpSavp:
    593    case SdpMediaSection::kDccpTlsRtpSavp:
    594    case SdpMediaSection::kUdpMbmsFecRtpAvp:
    595    case SdpMediaSection::kUdpMbmsFecRtpSavp:
    596    case SdpMediaSection::kUdpMbmsRepair:
    597    case SdpMediaSection::kFecUdp:
    598    case SdpMediaSection::kUdpFec:
    599    case SdpMediaSection::kTcpMrcpv2:
    600    case SdpMediaSection::kTcpTlsMrcpv2:
    601    case SdpMediaSection::kPstn:
    602    case SdpMediaSection::kUdpTlsUdptl:
    603    case SdpMediaSection::kSctp:
    604    case SdpMediaSection::kDtlsSctp:
    605    case SdpMediaSection::kUdpDtlsSctp:
    606    case SdpMediaSection::kTcpDtlsSctp:
    607      return false;
    608  }
    609  MOZ_CRASH("Unknown protocol, probably corruption.");
    610 }
    611 
    612 SdpMediaSection::Protocol SdpHelper::GetProtocolForMediaType(
    613    SdpMediaSection::MediaType type) {
    614  if (type == SdpMediaSection::kApplication) {
    615    return SdpMediaSection::kUdpDtlsSctp;
    616  }
    617 
    618  return SdpMediaSection::kUdpTlsRtpSavpf;
    619 }
    620 
    621 void SdpHelper::AppendSdpParseErrors(
    622    const std::vector<std::pair<size_t, std::string> >& aErrors,
    623    std::string* aErrorString) {
    624  std::ostringstream os;
    625  for (auto i = aErrors.begin(); i != aErrors.end(); ++i) {
    626    os << "SDP Parse Error on line " << i->first << ": " + i->second << '\n';
    627  }
    628  *aErrorString += os.str();
    629 }
    630 
    631 /* static */
    632 bool SdpHelper::GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam) {
    633  char* end;
    634  unsigned long pt = strtoul(ptString.c_str(), &end, 10);
    635  size_t length = static_cast<size_t>(end - ptString.c_str());
    636  if ((pt > UINT16_MAX) || (length != ptString.size())) {
    637    return false;
    638  }
    639  *ptOutparam = pt;
    640  return true;
    641 }
    642 
    643 void SdpHelper::NegotiateAndAddExtmaps(
    644    const SdpMediaSection& remoteMsection,
    645    std::vector<SdpExtmapAttributeList::Extmap>& localExtensions,
    646    SdpMediaSection* localMsection) {
    647  if (!remoteMsection.GetAttributeList().HasAttribute(
    648          SdpAttribute::kExtmapAttribute)) {
    649    return;
    650  }
    651 
    652  UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList);
    653  auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps;
    654  for (const auto& theirExt : theirExtmap) {
    655    for (auto& ourExt : localExtensions) {
    656      if (theirExt.entry == 0) {
    657        // 0 is invalid, ignore it
    658        continue;
    659      }
    660 
    661      if (theirExt.extensionname != ourExt.extensionname) {
    662        continue;
    663      }
    664 
    665      ourExt.direction = reverse(theirExt.direction) & ourExt.direction;
    666      if (ourExt.direction == SdpDirectionAttribute::Direction::kInactive) {
    667        continue;
    668      }
    669 
    670      // RFC 5285 says that ids >= 4096 can be used by the offerer to
    671      // force the answerer to pick, otherwise the value in the offer is
    672      // used.
    673      if (theirExt.entry < 4096) {
    674        ourExt.entry = theirExt.entry;
    675      }
    676 
    677      localExtmap->mExtmaps.push_back(ourExt);
    678    }
    679  }
    680 
    681  if (!localExtmap->mExtmaps.empty()) {
    682    localMsection->GetAttributeList().SetAttribute(localExtmap.release());
    683  }
    684 }
    685 
    686 static bool AttributeListMatch(const SdpAttributeList& list1,
    687                               const SdpAttributeList& list2) {
    688  // TODO: Consider adding telemetry in this function to record which
    689  // attributes don't match. See Bug 1432955.
    690  for (int i = SdpAttribute::kFirstAttribute; i <= SdpAttribute::kLastAttribute;
    691       i++) {
    692    auto attributeType = static_cast<SdpAttribute::AttributeType>(i);
    693    // TODO: We should do more thorough checking here, e.g. serialize and
    694    // compare strings. See Bug 1439690.
    695    if (list1.HasAttribute(attributeType, false) !=
    696        list2.HasAttribute(attributeType, false)) {
    697      return false;
    698    }
    699  }
    700  return true;
    701 }
    702 
    703 static bool MediaSectionMatch(const SdpMediaSection& mediaSection1,
    704                              const SdpMediaSection& mediaSection2) {
    705  // TODO: We should do more thorough checking in this function.
    706  // See Bug 1439690.
    707  if (!AttributeListMatch(mediaSection1.GetAttributeList(),
    708                          mediaSection2.GetAttributeList())) {
    709    return false;
    710  }
    711  if (mediaSection1.GetPort() != mediaSection2.GetPort()) {
    712    return false;
    713  }
    714  const std::vector<std::string>& formats1 = mediaSection1.GetFormats();
    715  const std::vector<std::string>& formats2 = mediaSection2.GetFormats();
    716  auto formats1Set = std::set<std::string>(formats1.begin(), formats1.end());
    717  auto formats2Set = std::set<std::string>(formats2.begin(), formats2.end());
    718  if (formats1Set != formats2Set) {
    719    return false;
    720  }
    721  return true;
    722 }
    723 
    724 bool SdpHelper::SdpMatch(const Sdp& sdp1, const Sdp& sdp2) {
    725  if (sdp1.GetMediaSectionCount() != sdp2.GetMediaSectionCount()) {
    726    return false;
    727  }
    728  if (!AttributeListMatch(sdp1.GetAttributeList(), sdp2.GetAttributeList())) {
    729    return false;
    730  }
    731  for (size_t i = 0; i < sdp1.GetMediaSectionCount(); i++) {
    732    const SdpMediaSection& mediaSection1 = sdp1.GetMediaSection(i);
    733    const SdpMediaSection& mediaSection2 = sdp2.GetMediaSection(i);
    734    if (!MediaSectionMatch(mediaSection1, mediaSection2)) {
    735      return false;
    736    }
    737  }
    738 
    739  return true;
    740 }
    741 
    742 nsresult SdpHelper::ValidateTransportAttributes(const Sdp& aSdp,
    743                                                sdp::SdpType aType) {
    744  BundledMids bundledMids;
    745  nsresult rv = GetBundledMids(aSdp, &bundledMids);
    746  NS_ENSURE_SUCCESS(rv, rv);
    747 
    748  for (size_t level = 0; level < aSdp.GetMediaSectionCount(); ++level) {
    749    const auto& msection = aSdp.GetMediaSection(level);
    750    if (OwnsTransport(msection, bundledMids, aType)) {
    751      const auto& mediaAttrs = msection.GetAttributeList();
    752      if (mediaAttrs.GetIceUfrag().empty()) {
    753        SDP_SET_ERROR("Invalid description, no ice-ufrag attribute at level "
    754                      << level);
    755        return NS_ERROR_INVALID_ARG;
    756      }
    757 
    758      if (mediaAttrs.GetIcePwd().empty()) {
    759        SDP_SET_ERROR("Invalid description, no ice-pwd attribute at level "
    760                      << level);
    761        return NS_ERROR_INVALID_ARG;
    762      }
    763 
    764      if (!mediaAttrs.HasAttribute(SdpAttribute::kFingerprintAttribute)) {
    765        SDP_SET_ERROR("Invalid description, no fingerprint attribute at level "
    766                      << level);
    767        return NS_ERROR_INVALID_ARG;
    768      }
    769 
    770      const SdpFingerprintAttributeList& fingerprints(
    771          mediaAttrs.GetFingerprint());
    772      if (fingerprints.mFingerprints.empty()) {
    773        SDP_SET_ERROR(
    774            "Invalid description, no supported fingerprint algorithms present "
    775            "at level "
    776            << level);
    777        return NS_ERROR_INVALID_ARG;
    778      }
    779 
    780      if (mediaAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true)) {
    781        if (mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kHoldconn) {
    782          SDP_SET_ERROR(
    783              "Invalid description, illegal setup attribute \"holdconn\" "
    784              "at level "
    785              << level);
    786          return NS_ERROR_INVALID_ARG;
    787        }
    788 
    789        if (aType == sdp::kAnswer &&
    790            mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kActpass) {
    791          SDP_SET_ERROR(
    792              "Invalid answer, illegal setup attribute \"actpass\" at level "
    793              << level);
    794          return NS_ERROR_INVALID_ARG;
    795        }
    796      } else if (aType == sdp::kOffer) {
    797        SDP_SET_ERROR("Invalid offer, no setup attribute at level " << level);
    798        return NS_ERROR_INVALID_ARG;
    799      }
    800    }
    801  }
    802  return NS_OK;
    803 }
    804 
    805 }  // namespace mozilla