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