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 "signaling/src/sdp/SipccSdpMediaSection.h"
8 
9 #include <ostream>
10 #include "signaling/src/sdp/SdpErrorHolder.h"
11 
12 #ifdef CRLF
13 #undef CRLF
14 #endif
15 #define CRLF "\r\n"
16 
17 namespace mozilla
18 {
19 
20 unsigned int
GetPort() const21 SipccSdpMediaSection::GetPort() const
22 {
23   return mPort;
24 }
25 
26 void
SetPort(unsigned int port)27 SipccSdpMediaSection::SetPort(unsigned int port)
28 {
29   mPort = port;
30 }
31 
32 unsigned int
GetPortCount() const33 SipccSdpMediaSection::GetPortCount() const
34 {
35   return mPortCount;
36 }
37 
38 SdpMediaSection::Protocol
GetProtocol() const39 SipccSdpMediaSection::GetProtocol() const
40 {
41   return mProtocol;
42 }
43 
44 const SdpConnection&
GetConnection() const45 SipccSdpMediaSection::GetConnection() const
46 {
47   return *mConnection;
48 }
49 
50 SdpConnection&
GetConnection()51 SipccSdpMediaSection::GetConnection()
52 {
53   return *mConnection;
54 }
55 
56 uint32_t
GetBandwidth(const std::string & type) const57 SipccSdpMediaSection::GetBandwidth(const std::string& type) const
58 {
59   auto found = mBandwidths.find(type);
60   if (found == mBandwidths.end()) {
61     return 0;
62   }
63   return found->second;
64 }
65 
66 const std::vector<std::string>&
GetFormats() const67 SipccSdpMediaSection::GetFormats() const
68 {
69   return mFormats;
70 }
71 
72 const SdpAttributeList&
GetAttributeList() const73 SipccSdpMediaSection::GetAttributeList() const
74 {
75   return mAttributeList;
76 }
77 
78 SdpAttributeList&
GetAttributeList()79 SipccSdpMediaSection::GetAttributeList()
80 {
81   return mAttributeList;
82 }
83 
84 SdpDirectionAttribute
GetDirectionAttribute() const85 SipccSdpMediaSection::GetDirectionAttribute() const
86 {
87   return SdpDirectionAttribute(mAttributeList.GetDirection());
88 }
89 
90 bool
Load(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)91 SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
92                            SdpErrorHolder& errorHolder)
93 {
94   switch (sdp_get_media_type(sdp, level)) {
95     case SDP_MEDIA_AUDIO:
96       mMediaType = kAudio;
97       break;
98     case SDP_MEDIA_VIDEO:
99       mMediaType = kVideo;
100       break;
101     case SDP_MEDIA_APPLICATION:
102       mMediaType = kApplication;
103       break;
104     case SDP_MEDIA_TEXT:
105       mMediaType = kText;
106       break;
107 
108     default:
109       errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
110                                 "Unsupported media section type");
111       return false;
112   }
113 
114   mPort = sdp_get_media_portnum(sdp, level);
115   int32_t pc = sdp_get_media_portcount(sdp, level);
116   if (pc == SDP_INVALID_VALUE) {
117     // SDP_INVALID_VALUE (ie; -2) is used when there is no port count. :(
118     mPortCount = 0;
119   } else if (pc > static_cast<int32_t>(UINT16_MAX) || pc < 0) {
120     errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
121                               "Invalid port count");
122     return false;
123   } else {
124     mPortCount = pc;
125   }
126 
127   if (!LoadProtocol(sdp, level, errorHolder)) {
128     return false;
129   }
130 
131   if (!LoadFormats(sdp, level, errorHolder)) {
132     return false;
133   }
134 
135   if (!mAttributeList.Load(sdp, level, errorHolder)) {
136     return false;
137   }
138 
139   if (!ValidateSimulcast(sdp, level, errorHolder)) {
140     return false;
141   }
142 
143   if (!mBandwidths.Load(sdp, level, errorHolder)) {
144     return false;
145   }
146 
147   return LoadConnection(sdp, level, errorHolder);
148 }
149 
150 bool
LoadProtocol(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)151 SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level,
152                                    SdpErrorHolder& errorHolder)
153 {
154   switch (sdp_get_media_transport(sdp, level)) {
155     case SDP_TRANSPORT_RTPAVP:
156       mProtocol = kRtpAvp;
157       break;
158     case SDP_TRANSPORT_RTPSAVP:
159       mProtocol = kRtpSavp;
160       break;
161     case SDP_TRANSPORT_RTPAVPF:
162       mProtocol = kRtpAvpf;
163       break;
164     case SDP_TRANSPORT_RTPSAVPF:
165       mProtocol = kRtpSavpf;
166       break;
167     case SDP_TRANSPORT_UDPTLSRTPSAVP:
168       mProtocol = kUdpTlsRtpSavp;
169       break;
170     case SDP_TRANSPORT_UDPTLSRTPSAVPF:
171       mProtocol = kUdpTlsRtpSavpf;
172       break;
173     case SDP_TRANSPORT_TCPTLSRTPSAVP:
174       mProtocol = kTcpTlsRtpSavp;
175       break;
176     case SDP_TRANSPORT_TCPTLSRTPSAVPF:
177       mProtocol = kTcpTlsRtpSavpf;
178       break;
179     case SDP_TRANSPORT_DTLSSCTP:
180       mProtocol = kDtlsSctp;
181       break;
182 
183     default:
184       errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
185                                 "Unsupported media transport type");
186       return false;
187   }
188   return true;
189 }
190 
191 bool
LoadFormats(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)192 SipccSdpMediaSection::LoadFormats(sdp_t* sdp,
193                                   uint16_t level,
194                                   SdpErrorHolder& errorHolder)
195 {
196   sdp_media_e mtype = sdp_get_media_type(sdp, level);
197 
198   if (mtype == SDP_MEDIA_APPLICATION) {
199     uint32_t ptype = sdp_get_media_sctp_port(sdp, level);
200     std::ostringstream osPayloadType;
201     osPayloadType << ptype;
202     mFormats.push_back(osPayloadType.str());
203   } else if (mtype == SDP_MEDIA_AUDIO || mtype == SDP_MEDIA_VIDEO) {
204     uint16_t count = sdp_get_media_num_payload_types(sdp, level);
205     for (uint16_t i = 0; i < count; ++i) {
206       sdp_payload_ind_e indicator; // we ignore this, which is fine
207       uint32_t ptype =
208           sdp_get_media_payload_type(sdp, level, i + 1, &indicator);
209 
210       if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) {
211         errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
212                                   "Format is too large");
213         return false;
214       }
215 
216       std::ostringstream osPayloadType;
217       // sipcc stores payload types in a funny way. When sipcc and the SDP it
218       // parsed differ on what payload type number should be used for a given
219       // codec, sipcc's value goes in the lower byte, and the SDP's value in
220       // the upper byte. When they do not differ, only the lower byte is used.
221       // We want what was in the SDP, verbatim.
222       osPayloadType << GET_DYN_PAYLOAD_TYPE_VALUE(ptype);
223       mFormats.push_back(osPayloadType.str());
224     }
225   }
226 
227   return true;
228 }
229 
230 bool
ValidateSimulcast(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder) const231 SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level,
232                                         SdpErrorHolder& errorHolder) const
233 {
234   if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) {
235     return true;
236   }
237 
238   const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast());
239   if (!ValidateSimulcastVersions(
240         sdp, level, simulcast.sendVersions, sdp::kSend, errorHolder)) {
241     return false;
242   }
243   if (!ValidateSimulcastVersions(
244         sdp, level, simulcast.recvVersions, sdp::kRecv, errorHolder)) {
245     return false;
246   }
247   return true;
248 }
249 
250 bool
ValidateSimulcastVersions(sdp_t * sdp,uint16_t level,const SdpSimulcastAttribute::Versions & versions,sdp::Direction direction,SdpErrorHolder & errorHolder) const251 SipccSdpMediaSection::ValidateSimulcastVersions(
252     sdp_t* sdp,
253     uint16_t level,
254     const SdpSimulcastAttribute::Versions& versions,
255     sdp::Direction direction,
256     SdpErrorHolder& errorHolder) const
257 {
258   if (versions.IsSet() && !(direction & GetDirectionAttribute().mValue)) {
259     errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
260                               "simulcast attribute has a direction that is "
261                               "inconsistent with the direction of this media "
262                               "section.");
263     return false;
264   }
265 
266   for (const SdpSimulcastAttribute::Version& version : versions) {
267     for (const std::string& id : version.choices) {
268       if (versions.type == SdpSimulcastAttribute::Versions::kRid) {
269         const SdpRidAttributeList::Rid* ridAttr = FindRid(id);
270         if (!ridAttr || (ridAttr->direction != direction)) {
271           std::ostringstream os;
272           os << "No rid attribute for \'" << id << "\'";
273           errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
274                                     os.str());
275           return false;
276         }
277       } else if (versions.type == SdpSimulcastAttribute::Versions::kPt) {
278         if (std::find(mFormats.begin(), mFormats.end(), id)
279             == mFormats.end()) {
280           std::ostringstream os;
281           os << "No pt for \'" << id << "\'";
282           errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
283                                     os.str());
284           return false;
285         }
286       }
287     }
288   }
289   return true;
290 }
291 
292 bool
LoadConnection(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)293 SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
294                                      SdpErrorHolder& errorHolder)
295 {
296   if (!sdp_connection_valid(sdp, level)) {
297     level = SDP_SESSION_LEVEL;
298     if (!sdp_connection_valid(sdp, level)) {
299       errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
300                                 "Missing c= line");
301       return false;
302     }
303   }
304 
305   sdp_nettype_e type = sdp_get_conn_nettype(sdp, level);
306   if (type != SDP_NT_INTERNET) {
307     errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
308                               "Unsupported network type");
309     return false;
310   }
311 
312   sdp::AddrType addrType;
313   switch (sdp_get_conn_addrtype(sdp, level)) {
314     case SDP_AT_IP4:
315       addrType = sdp::kIPv4;
316       break;
317     case SDP_AT_IP6:
318       addrType = sdp::kIPv6;
319       break;
320     default:
321       errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
322                                 "Unsupported address type");
323       return false;
324   }
325 
326   std::string address = sdp_get_conn_address(sdp, level);
327   int16_t ttl = static_cast<uint16_t>(sdp_get_mcast_ttl(sdp, level));
328   if (ttl < 0) {
329     ttl = 0;
330   }
331   int32_t numAddr =
332       static_cast<uint32_t>(sdp_get_mcast_num_of_addresses(sdp, level));
333   if (numAddr < 0) {
334     numAddr = 0;
335   }
336   mConnection = MakeUnique<SdpConnection>(addrType, address, ttl, numAddr);
337   return true;
338 }
339 
340 void
AddCodec(const std::string & pt,const std::string & name,uint32_t clockrate,uint16_t channels)341 SipccSdpMediaSection::AddCodec(const std::string& pt, const std::string& name,
342                                uint32_t clockrate, uint16_t channels)
343 {
344   mFormats.push_back(pt);
345 
346   SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList();
347   if (mAttributeList.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
348     const SdpRtpmapAttributeList& old = mAttributeList.GetRtpmap();
349     for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) {
350       rtpmap->mRtpmaps.push_back(*it);
351     }
352   }
353   SdpRtpmapAttributeList::CodecType codec = SdpRtpmapAttributeList::kOtherCodec;
354   if (name == "opus") {
355     codec = SdpRtpmapAttributeList::kOpus;
356   } else if (name == "G722") {
357     codec = SdpRtpmapAttributeList::kG722;
358   } else if (name == "PCMU") {
359     codec = SdpRtpmapAttributeList::kPCMU;
360   } else if (name == "PCMA") {
361     codec = SdpRtpmapAttributeList::kPCMA;
362   } else if (name == "VP8") {
363     codec = SdpRtpmapAttributeList::kVP8;
364   } else if (name == "VP9") {
365     codec = SdpRtpmapAttributeList::kVP9;
366   } else if (name == "H264") {
367     codec = SdpRtpmapAttributeList::kH264;
368   }
369 
370   rtpmap->PushEntry(pt, codec, name, clockrate, channels);
371   mAttributeList.SetAttribute(rtpmap);
372 }
373 
374 void
ClearCodecs()375 SipccSdpMediaSection::ClearCodecs()
376 {
377   mFormats.clear();
378   mAttributeList.RemoveAttribute(SdpAttribute::kRtpmapAttribute);
379   mAttributeList.RemoveAttribute(SdpAttribute::kFmtpAttribute);
380   mAttributeList.RemoveAttribute(SdpAttribute::kSctpmapAttribute);
381   mAttributeList.RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
382 }
383 
384 void
AddDataChannel(const std::string & pt,const std::string & name,uint16_t streams)385 SipccSdpMediaSection::AddDataChannel(const std::string& pt,
386                                      const std::string& name, uint16_t streams)
387 {
388   // Only one allowed, for now. This may change as the specs (and deployments)
389   // evolve.
390   mFormats.clear();
391   mFormats.push_back(pt);
392   SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList();
393   sctpmap->PushEntry(pt, name, streams);
394   mAttributeList.SetAttribute(sctpmap);
395 }
396 
397 void
Serialize(std::ostream & os) const398 SipccSdpMediaSection::Serialize(std::ostream& os) const
399 {
400   os << "m=" << mMediaType << " " << mPort;
401   if (mPortCount) {
402     os << "/" << mPortCount;
403   }
404   os << " " << mProtocol;
405   for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
406     os << " " << (*i);
407   }
408   os << CRLF;
409 
410   // We dont do i=
411 
412   if (mConnection) {
413     os << *mConnection;
414   }
415 
416   mBandwidths.Serialize(os);
417 
418   // We dont do k= because they're evil
419 
420   os << mAttributeList;
421 }
422 
423 } // namespace mozilla
424