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