diff options
Diffstat (limited to 'dom/media/webrtc/sdp/SdpHelper.cpp')
-rw-r--r-- | dom/media/webrtc/sdp/SdpHelper.cpp | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/dom/media/webrtc/sdp/SdpHelper.cpp b/dom/media/webrtc/sdp/SdpHelper.cpp new file mode 100644 index 0000000000..9788b610ca --- /dev/null +++ b/dom/media/webrtc/sdp/SdpHelper.cpp @@ -0,0 +1,800 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "sdp/SdpHelper.h" + +#include "sdp/Sdp.h" +#include "sdp/SdpMediaSection.h" +#include "transport/logging.h" + +#include "nsDebug.h" +#include "nsError.h" +#include "prprf.h" + +#include <string.h> +#include <set> + +namespace mozilla { +MOZ_MTLOG_MODULE("sdp") + +#define SDP_SET_ERROR(error) \ + do { \ + std::ostringstream os; \ + os << error; \ + mLastError = os.str(); \ + MOZ_MTLOG(ML_ERROR, mLastError); \ + } while (0); + +nsresult SdpHelper::CopyTransportParams(size_t numComponents, + const SdpMediaSection& oldLocal, + SdpMediaSection* newLocal) { + const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList(); + // Copy over m-section details + if (!oldLocalAttrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) { + // Do not copy port 0 from an offer with a=bundle-only; this could cause + // an answer msection to be erroneously rejected. + newLocal->SetPort(oldLocal.GetPort()); + } + newLocal->GetConnection() = oldLocal.GetConnection(); + + SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList(); + + // Now we copy over attributes that won't be added by the usual logic + if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) && + numComponents) { + UniquePtr<SdpMultiStringAttribute> candidateAttrs( + new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute)); + for (const std::string& candidate : oldLocalAttrs.GetCandidate()) { + size_t component; + nsresult rv = GetComponent(candidate, &component); + NS_ENSURE_SUCCESS(rv, rv); + if (numComponents >= component) { + candidateAttrs->mValues.push_back(candidate); + } + } + if (!candidateAttrs->mValues.empty()) { + newLocalAttrs.SetAttribute(candidateAttrs.release()); + } + } + + if (oldLocalAttrs.HasAttribute(SdpAttribute::kEndOfCandidatesAttribute)) { + newLocalAttrs.SetAttribute( + new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute)); + } + + if (numComponents == 2 && + oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) { + // copy rtcp attribute if we had one that we are using + newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp())); + } + + return NS_OK; +} + +bool SdpHelper::AreOldTransportParamsValid(const Sdp& oldAnswer, + const Sdp& offerersPreviousSdp, + const Sdp& newOffer, size_t level) { + if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) || + MsectionIsDisabled(newOffer.GetMediaSection(level))) { + // Obvious + return false; + } + + if (!OwnsTransport(oldAnswer, level, sdp::kAnswer)) { + // The transport attributes on this m-section were thrown away, because it + // was bundled. + return false; + } + + if (!OwnsTransport(newOffer, level, sdp::kOffer)) { + return false; + } + + if (IceCredentialsDiffer(newOffer.GetMediaSection(level), + offerersPreviousSdp.GetMediaSection(level))) { + return false; + } + + return true; +} + +bool SdpHelper::IceCredentialsDiffer(const SdpMediaSection& msection1, + const SdpMediaSection& msection2) { + const SdpAttributeList& attrs1(msection1.GetAttributeList()); + const SdpAttributeList& attrs2(msection2.GetAttributeList()); + + if ((attrs1.GetIceUfrag() != attrs2.GetIceUfrag()) || + (attrs1.GetIcePwd() != attrs2.GetIcePwd())) { + return true; + } + + return false; +} + +nsresult SdpHelper::GetComponent(const std::string& candidate, + size_t* component) { + unsigned int temp; + int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp); + if (result == 1) { + *component = temp; + return NS_OK; + } + SDP_SET_ERROR("Malformed ICE candidate: " << candidate); + return NS_ERROR_INVALID_ARG; +} + +bool SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const { + return !msection.GetPort() && !msection.GetAttributeList().HasAttribute( + SdpAttribute::kBundleOnlyAttribute); +} + +void SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection) { + std::string mid; + + // Make sure to remove the mid from any group attributes + if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) { + mid = msection->GetAttributeList().GetMid(); + if (sdp->GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) { + UniquePtr<SdpGroupAttributeList> newGroupAttr( + new SdpGroupAttributeList(sdp->GetAttributeList().GetGroup())); + newGroupAttr->RemoveMid(mid); + sdp->GetAttributeList().SetAttribute(newGroupAttr.release()); + } + } + + // Clear out attributes. + msection->GetAttributeList().Clear(); + + auto* direction = new SdpDirectionAttribute(SdpDirectionAttribute::kInactive); + msection->GetAttributeList().SetAttribute(direction); + msection->SetPort(0); + + // maintain the mid for easier identification on other side + if (!mid.empty()) { + msection->GetAttributeList().SetAttribute( + new SdpStringAttribute(SdpAttribute::kMidAttribute, mid)); + } + + msection->ClearCodecs(); + + auto mediaType = msection->GetMediaType(); + switch (mediaType) { + case SdpMediaSection::kAudio: + msection->AddCodec("0", "PCMU", 8000, 1); + break; + case SdpMediaSection::kVideo: + msection->AddCodec("120", "VP8", 90000, 1); + break; + case SdpMediaSection::kApplication: + msection->AddDataChannel("webrtc-datachannel", 0, 0, 0); + break; + default: + // We need to have something here to fit the grammar, this seems safe + // and 19 is a reserved payload type which should not be used by anyone. + msection->AddCodec("19", "reserved", 8000, 1); + } +} + +void SdpHelper::GetBundleGroups( + const Sdp& sdp, + std::vector<SdpGroupAttributeList::Group>* bundleGroups) const { + if (sdp.GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) { + for (auto& group : sdp.GetAttributeList().GetGroup().mGroups) { + if (group.semantics == SdpGroupAttributeList::kBundle) { + bundleGroups->push_back(group); + } + } + } +} + +nsresult SdpHelper::GetBundledMids(const Sdp& sdp, BundledMids* bundledMids) { + std::vector<SdpGroupAttributeList::Group> bundleGroups; + GetBundleGroups(sdp, &bundleGroups); + + for (SdpGroupAttributeList::Group& group : bundleGroups) { + if (group.tags.empty()) { + continue; + } + + const SdpMediaSection* msection(FindMsectionByMid(sdp, group.tags[0])); + + if (!msection) { + SDP_SET_ERROR( + "mid specified for bundle transport in group attribute" + " does not exist in the SDP. (mid=" + << group.tags[0] << ")"); + return NS_ERROR_INVALID_ARG; + } + + if (MsectionIsDisabled(*msection)) { + SDP_SET_ERROR( + "mid specified for bundle transport in group attribute" + " points at a disabled m-section. (mid=" + << group.tags[0] << ")"); + return NS_ERROR_INVALID_ARG; + } + + for (const std::string& mid : group.tags) { + if (bundledMids->count(mid)) { + SDP_SET_ERROR("mid \'" << mid + << "\' appears more than once in a " + "BUNDLE group"); + return NS_ERROR_INVALID_ARG; + } + + (*bundledMids)[mid] = msection; + } + } + + return NS_OK; +} + +bool SdpHelper::OwnsTransport(const Sdp& sdp, uint16_t level, + sdp::SdpType type) { + auto& msection = sdp.GetMediaSection(level); + + BundledMids bundledMids; + nsresult rv = GetBundledMids(sdp, &bundledMids); + if (NS_FAILED(rv)) { + // Should have been caught sooner. + MOZ_ASSERT(false); + return true; + } + + return OwnsTransport(msection, bundledMids, type); +} + +bool SdpHelper::OwnsTransport(const SdpMediaSection& msection, + const BundledMids& bundledMids, + sdp::SdpType type) { + if (MsectionIsDisabled(msection)) { + return false; + } + + if (!msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) { + // No mid, definitely no bundle for this m-section + return true; + } + std::string mid(msection.GetAttributeList().GetMid()); + if (type != sdp::kOffer || msection.GetAttributeList().HasAttribute( + SdpAttribute::kBundleOnlyAttribute)) { + // If this is an answer, or this m-section is marked bundle-only, the group + // attribute is authoritative. Otherwise, we aren't sure. + if (bundledMids.count(mid) && &msection != bundledMids.at(mid)) { + // mid is bundled, and isn't the bundle m-section + return false; + } + } + + return true; +} + +nsresult SdpHelper::GetMidFromLevel(const Sdp& sdp, uint16_t level, + std::string* mid) { + if (level >= sdp.GetMediaSectionCount()) { + SDP_SET_ERROR("Index " << level << " out of range"); + return NS_ERROR_INVALID_ARG; + } + + const SdpMediaSection& msection = sdp.GetMediaSection(level); + const SdpAttributeList& attrList = msection.GetAttributeList(); + + // grab the mid and set the outparam + if (attrList.HasAttribute(SdpAttribute::kMidAttribute)) { + *mid = attrList.GetMid(); + } + + return NS_OK; +} + +nsresult SdpHelper::AddCandidateToSdp(Sdp* sdp, + const std::string& candidateUntrimmed, + uint16_t level, + const std::string& ufrag) { + if (level >= sdp->GetMediaSectionCount()) { + SDP_SET_ERROR("Index " << level << " out of range"); + return NS_ERROR_INVALID_ARG; + } + + SdpMediaSection& msection = sdp->GetMediaSection(level); + SdpAttributeList& attrList = msection.GetAttributeList(); + + if (!ufrag.empty()) { + if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) || + attrList.GetIceUfrag() != ufrag) { + SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")"); + return NS_ERROR_INVALID_ARG; + } + } + + if (candidateUntrimmed.empty()) { + SetIceGatheringComplete(sdp, level, ufrag); + return NS_OK; + } + + // Trim off '[a=]candidate:' + size_t begin = candidateUntrimmed.find(':'); + if (begin == std::string::npos) { + SDP_SET_ERROR("Invalid candidate, no ':' (" << candidateUntrimmed << ")"); + return NS_ERROR_INVALID_ARG; + } + ++begin; + + std::string candidate = candidateUntrimmed.substr(begin); + + UniquePtr<SdpMultiStringAttribute> candidates; + if (!attrList.HasAttribute(SdpAttribute::kCandidateAttribute)) { + // Create new + candidates.reset( + new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute)); + } else { + // Copy existing + candidates.reset(new SdpMultiStringAttribute( + *static_cast<const SdpMultiStringAttribute*>( + attrList.GetAttribute(SdpAttribute::kCandidateAttribute)))); + } + candidates->PushEntry(candidate); + attrList.SetAttribute(candidates.release()); + + return NS_OK; +} + +nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp, + const std::string& ufrag) { + for (uint16_t i = 0; i < sdp->GetMediaSectionCount(); ++i) { + nsresult rv = SetIceGatheringComplete(sdp, i, ufrag); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp, uint16_t level, + const std::string& ufrag) { + if (level >= sdp->GetMediaSectionCount()) { + SDP_SET_ERROR("Index " << level << " out of range"); + return NS_ERROR_INVALID_ARG; + } + + SdpMediaSection& msection = sdp->GetMediaSection(level); + SdpAttributeList& attrList = msection.GetAttributeList(); + + if (!ufrag.empty()) { + if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) || + attrList.GetIceUfrag() != ufrag) { + SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")"); + return NS_ERROR_INVALID_ARG; + } + } + + attrList.SetAttribute( + new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute)); + // Remove trickle-ice option + attrList.RemoveAttribute(SdpAttribute::kIceOptionsAttribute); + return NS_OK; +} + +void SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr, + uint16_t defaultCandidatePort, + const std::string& defaultRtcpCandidateAddr, + uint16_t defaultRtcpCandidatePort, + SdpMediaSection* msection) { + SdpAttributeList& attrList = msection->GetAttributeList(); + + msection->GetConnection().SetAddress(defaultCandidateAddr); + msection->SetPort(defaultCandidatePort); + if (!defaultRtcpCandidateAddr.empty()) { + sdp::AddrType ipVersion = sdp::kIPv4; + if (defaultRtcpCandidateAddr.find(':') != std::string::npos) { + ipVersion = sdp::kIPv6; + } + attrList.SetAttribute(new SdpRtcpAttribute(defaultRtcpCandidatePort, + sdp::kInternet, ipVersion, + defaultRtcpCandidateAddr)); + } +} + +nsresult SdpHelper::GetIdsFromMsid(const Sdp& sdp, + const SdpMediaSection& msection, + std::vector<std::string>* streamIds) { + std::vector<SdpMsidAttributeList::Msid> allMsids; + nsresult rv = GetMsids(msection, &allMsids); + NS_ENSURE_SUCCESS(rv, rv); + + if (allMsids.empty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + streamIds->clear(); + for (const auto& msid : allMsids) { + // "-" means no stream, see draft-ietf-mmusic-msid + // Remove duplicates, but leave order the same + if (msid.identifier != "-" && + !std::count(streamIds->begin(), streamIds->end(), msid.identifier)) { + streamIds->push_back(msid.identifier); + } + } + + return NS_OK; +} + +nsresult SdpHelper::GetMsids(const SdpMediaSection& msection, + std::vector<SdpMsidAttributeList::Msid>* msids) { + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { + *msids = msection.GetAttributeList().GetMsid().mMsids; + return NS_OK; + } + + // If there are no a=msid, can we find msids in ssrc attributes? + // (Chrome does not put plain-old msid attributes in its SDP) + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) { + auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs; + + for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) { + if (i->attribute.find("msid:") == 0) { + std::string streamId; + std::string trackId; + nsresult rv = ParseMsid(i->attribute, &streamId, &trackId); + NS_ENSURE_SUCCESS(rv, rv); + msids->push_back({streamId, trackId}); + } + } + } + + return NS_OK; +} + +nsresult SdpHelper::ParseMsid(const std::string& msidAttribute, + std::string* streamId, std::string* trackId) { + // Would be nice if SdpSsrcAttributeList could parse out the contained + // attribute, but at least the parse here is simple. + // We are being very forgiving here wrt whitespace; tabs are not actually + // allowed, nor is leading/trailing whitespace. + size_t streamIdStart = msidAttribute.find_first_not_of(" \t", 5); + // We do not assume the appdata token is here, since this is not + // necessarily a webrtc msid + if (streamIdStart == std::string::npos) { + SDP_SET_ERROR("Malformed source-level msid attribute: " << msidAttribute); + return NS_ERROR_INVALID_ARG; + } + + size_t streamIdEnd = msidAttribute.find_first_of(" \t", streamIdStart); + if (streamIdEnd == std::string::npos) { + streamIdEnd = msidAttribute.size(); + } + + size_t trackIdStart = msidAttribute.find_first_not_of(" \t", streamIdEnd); + if (trackIdStart == std::string::npos) { + trackIdStart = msidAttribute.size(); + } + + size_t trackIdEnd = msidAttribute.find_first_of(" \t", trackIdStart); + if (trackIdEnd == std::string::npos) { + trackIdEnd = msidAttribute.size(); + } + + size_t streamIdSize = streamIdEnd - streamIdStart; + size_t trackIdSize = trackIdEnd - trackIdStart; + + *streamId = msidAttribute.substr(streamIdStart, streamIdSize); + *trackId = msidAttribute.substr(trackIdStart, trackIdSize); + return NS_OK; +} + +void SdpHelper::SetupMsidSemantic(const std::vector<std::string>& msids, + Sdp* sdp) const { + if (!msids.empty()) { + UniquePtr<SdpMsidSemanticAttributeList> msidSemantics( + new SdpMsidSemanticAttributeList); + msidSemantics->PushEntry("WMS", msids); + sdp->GetAttributeList().SetAttribute(msidSemantics.release()); + } +} + +std::string SdpHelper::GetCNAME(const SdpMediaSection& msection) const { + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) { + auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs; + for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) { + if (i->attribute.find("cname:") == 0) { + return i->attribute.substr(6); + } + } + } + return ""; +} + +const SdpMediaSection* SdpHelper::FindMsectionByMid( + const Sdp& sdp, const std::string& mid) const { + for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) { + auto& attrs = sdp.GetMediaSection(i).GetAttributeList(); + if (attrs.HasAttribute(SdpAttribute::kMidAttribute) && + attrs.GetMid() == mid) { + return &sdp.GetMediaSection(i); + } + } + return nullptr; +} + +SdpMediaSection* SdpHelper::FindMsectionByMid(Sdp& sdp, + const std::string& mid) const { + for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) { + auto& attrs = sdp.GetMediaSection(i).GetAttributeList(); + if (attrs.HasAttribute(SdpAttribute::kMidAttribute) && + attrs.GetMid() == mid) { + return &sdp.GetMediaSection(i); + } + } + return nullptr; +} + +nsresult SdpHelper::CopyStickyParams(const SdpMediaSection& source, + SdpMediaSection* dest) { + auto& sourceAttrs = source.GetAttributeList(); + auto& destAttrs = dest->GetAttributeList(); + + // There's no reason to renegotiate rtcp-mux + if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) { + destAttrs.SetAttribute( + new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute)); + } + + // mid should stay the same + if (sourceAttrs.HasAttribute(SdpAttribute::kMidAttribute)) { + destAttrs.SetAttribute(new SdpStringAttribute(SdpAttribute::kMidAttribute, + sourceAttrs.GetMid())); + } + + // Keep RTCP mode setting + if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpRsizeAttribute) && + source.GetMediaType() == SdpMediaSection::kVideo) { + destAttrs.SetAttribute( + new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute)); + } + + return NS_OK; +} + +bool SdpHelper::HasRtcp(SdpMediaSection::Protocol proto) const { + switch (proto) { + case SdpMediaSection::kRtpAvpf: + case SdpMediaSection::kDccpRtpAvpf: + case SdpMediaSection::kDccpRtpSavpf: + case SdpMediaSection::kRtpSavpf: + case SdpMediaSection::kUdpTlsRtpSavpf: + case SdpMediaSection::kTcpDtlsRtpSavpf: + case SdpMediaSection::kDccpTlsRtpSavpf: + return true; + case SdpMediaSection::kRtpAvp: + case SdpMediaSection::kUdp: + case SdpMediaSection::kVat: + case SdpMediaSection::kRtp: + case SdpMediaSection::kUdptl: + case SdpMediaSection::kTcp: + case SdpMediaSection::kTcpRtpAvp: + case SdpMediaSection::kRtpSavp: + case SdpMediaSection::kTcpBfcp: + case SdpMediaSection::kTcpTlsBfcp: + case SdpMediaSection::kTcpTls: + case SdpMediaSection::kFluteUdp: + case SdpMediaSection::kTcpMsrp: + case SdpMediaSection::kTcpTlsMsrp: + case SdpMediaSection::kDccp: + case SdpMediaSection::kDccpRtpAvp: + case SdpMediaSection::kDccpRtpSavp: + case SdpMediaSection::kUdpTlsRtpSavp: + case SdpMediaSection::kTcpDtlsRtpSavp: + case SdpMediaSection::kDccpTlsRtpSavp: + case SdpMediaSection::kUdpMbmsFecRtpAvp: + case SdpMediaSection::kUdpMbmsFecRtpSavp: + case SdpMediaSection::kUdpMbmsRepair: + case SdpMediaSection::kFecUdp: + case SdpMediaSection::kUdpFec: + case SdpMediaSection::kTcpMrcpv2: + case SdpMediaSection::kTcpTlsMrcpv2: + case SdpMediaSection::kPstn: + case SdpMediaSection::kUdpTlsUdptl: + case SdpMediaSection::kSctp: + case SdpMediaSection::kDtlsSctp: + case SdpMediaSection::kUdpDtlsSctp: + case SdpMediaSection::kTcpDtlsSctp: + return false; + } + MOZ_CRASH("Unknown protocol, probably corruption."); +} + +SdpMediaSection::Protocol SdpHelper::GetProtocolForMediaType( + SdpMediaSection::MediaType type) { + if (type == SdpMediaSection::kApplication) { + return SdpMediaSection::kUdpDtlsSctp; + } + + return SdpMediaSection::kUdpTlsRtpSavpf; +} + +void SdpHelper::AppendSdpParseErrors( + const std::vector<std::pair<size_t, std::string> >& aErrors, + std::string* aErrorString) { + std::ostringstream os; + for (auto i = aErrors.begin(); i != aErrors.end(); ++i) { + os << "SDP Parse Error on line " << i->first << ": " + i->second << '\n'; + } + *aErrorString += os.str(); +} + +/* static */ +bool SdpHelper::GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam) { + char* end; + unsigned long pt = strtoul(ptString.c_str(), &end, 10); + size_t length = static_cast<size_t>(end - ptString.c_str()); + if ((pt > UINT16_MAX) || (length != ptString.size())) { + return false; + } + *ptOutparam = pt; + return true; +} + +void SdpHelper::NegotiateAndAddExtmaps( + const SdpMediaSection& remoteMsection, + std::vector<SdpExtmapAttributeList::Extmap>& localExtensions, + SdpMediaSection* localMsection) { + if (!remoteMsection.GetAttributeList().HasAttribute( + SdpAttribute::kExtmapAttribute)) { + return; + } + + UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList); + auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps; + for (const auto& theirExt : theirExtmap) { + for (auto& ourExt : localExtensions) { + if (theirExt.entry == 0) { + // 0 is invalid, ignore it + continue; + } + + if (theirExt.extensionname != ourExt.extensionname) { + continue; + } + + ourExt.direction = reverse(theirExt.direction) & ourExt.direction; + if (ourExt.direction == SdpDirectionAttribute::Direction::kInactive) { + continue; + } + + // RFC 5285 says that ids >= 4096 can be used by the offerer to + // force the answerer to pick, otherwise the value in the offer is + // used. + if (theirExt.entry < 4096) { + ourExt.entry = theirExt.entry; + } + + localExtmap->mExtmaps.push_back(ourExt); + } + } + + if (!localExtmap->mExtmaps.empty()) { + localMsection->GetAttributeList().SetAttribute(localExtmap.release()); + } +} + +static bool AttributeListMatch(const SdpAttributeList& list1, + const SdpAttributeList& list2) { + // TODO: Consider adding telemetry in this function to record which + // attributes don't match. See Bug 1432955. + for (int i = SdpAttribute::kFirstAttribute; i <= SdpAttribute::kLastAttribute; + i++) { + auto attributeType = static_cast<SdpAttribute::AttributeType>(i); + // TODO: We should do more thorough checking here, e.g. serialize and + // compare strings. See Bug 1439690. + if (list1.HasAttribute(attributeType, false) != + list2.HasAttribute(attributeType, false)) { + return false; + } + } + return true; +} + +static bool MediaSectionMatch(const SdpMediaSection& mediaSection1, + const SdpMediaSection& mediaSection2) { + // TODO: We should do more thorough checking in this function. + // See Bug 1439690. + if (!AttributeListMatch(mediaSection1.GetAttributeList(), + mediaSection2.GetAttributeList())) { + return false; + } + if (mediaSection1.GetPort() != mediaSection2.GetPort()) { + return false; + } + const std::vector<std::string>& formats1 = mediaSection1.GetFormats(); + const std::vector<std::string>& formats2 = mediaSection2.GetFormats(); + auto formats1Set = std::set<std::string>(formats1.begin(), formats1.end()); + auto formats2Set = std::set<std::string>(formats2.begin(), formats2.end()); + if (formats1Set != formats2Set) { + return false; + } + return true; +} + +bool SdpHelper::SdpMatch(const Sdp& sdp1, const Sdp& sdp2) { + if (sdp1.GetMediaSectionCount() != sdp2.GetMediaSectionCount()) { + return false; + } + if (!AttributeListMatch(sdp1.GetAttributeList(), sdp2.GetAttributeList())) { + return false; + } + for (size_t i = 0; i < sdp1.GetMediaSectionCount(); i++) { + const SdpMediaSection& mediaSection1 = sdp1.GetMediaSection(i); + const SdpMediaSection& mediaSection2 = sdp2.GetMediaSection(i); + if (!MediaSectionMatch(mediaSection1, mediaSection2)) { + return false; + } + } + + return true; +} + +nsresult SdpHelper::ValidateTransportAttributes(const Sdp& aSdp, + sdp::SdpType aType) { + BundledMids bundledMids; + nsresult rv = GetBundledMids(aSdp, &bundledMids); + NS_ENSURE_SUCCESS(rv, rv); + + for (size_t level = 0; level < aSdp.GetMediaSectionCount(); ++level) { + const auto& msection = aSdp.GetMediaSection(level); + if (OwnsTransport(msection, bundledMids, aType)) { + const auto& mediaAttrs = msection.GetAttributeList(); + if (mediaAttrs.GetIceUfrag().empty()) { + SDP_SET_ERROR("Invalid description, no ice-ufrag attribute at level " + << level); + return NS_ERROR_INVALID_ARG; + } + + if (mediaAttrs.GetIcePwd().empty()) { + SDP_SET_ERROR("Invalid description, no ice-pwd attribute at level " + << level); + return NS_ERROR_INVALID_ARG; + } + + if (!mediaAttrs.HasAttribute(SdpAttribute::kFingerprintAttribute)) { + SDP_SET_ERROR("Invalid description, no fingerprint attribute at level " + << level); + return NS_ERROR_INVALID_ARG; + } + + const SdpFingerprintAttributeList& fingerprints( + mediaAttrs.GetFingerprint()); + if (fingerprints.mFingerprints.empty()) { + SDP_SET_ERROR( + "Invalid description, no supported fingerprint algorithms present " + "at level " + << level); + return NS_ERROR_INVALID_ARG; + } + + if (mediaAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true)) { + if (mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kHoldconn) { + SDP_SET_ERROR( + "Invalid description, illegal setup attribute \"holdconn\" " + "at level " + << level); + return NS_ERROR_INVALID_ARG; + } + + if (aType == sdp::kAnswer && + mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kActpass) { + SDP_SET_ERROR( + "Invalid answer, illegal setup attribute \"actpass\" at level " + << level); + return NS_ERROR_INVALID_ARG; + } + } else if (aType == sdp::kOffer) { + SDP_SET_ERROR("Invalid offer, no setup attribute at level " << level); + return NS_ERROR_INVALID_ARG; + } + } + } + return NS_OK; +} + +} // namespace mozilla |