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