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
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #![warn(clippy::all)]
6 #![forbid(unsafe_code)]
7
8 #[macro_use]
9 extern crate log;
10 #[cfg(feature = "serialize")]
11 #[macro_use]
12 extern crate serde_derive;
13 #[cfg(feature = "serialize")]
14 extern crate serde;
15 use std::convert::TryFrom;
16 use std::fmt;
17
18 #[macro_use]
19 pub mod attribute_type;
20 pub mod address;
21 pub mod anonymizer;
22 pub mod error;
23 pub mod media_type;
24 pub mod network;
25
26 use address::{AddressTyped, ExplicitlyTypedAddress};
27 use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
28 use attribute_type::{
29 parse_attribute, SdpAttribute, SdpAttributeRid, SdpAttributeSimulcastVersion, SdpAttributeType,
30 SdpSingleDirection,
31 };
32 use error::{SdpParserError, SdpParserInternalError};
33 use media_type::{
34 parse_media, parse_media_vector, SdpFormatList, SdpMedia, SdpMediaLine, SdpMediaValue,
35 SdpProtocolValue,
36 };
37 use network::{parse_address_type, parse_network_type};
38
39 /*
40 * RFC4566
41 * bandwidth-fields = *(%x62 "=" bwtype ":" bandwidth CRLF)
42 */
43 #[derive(Clone)]
44 #[cfg_attr(feature = "serialize", derive(Serialize))]
45 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
46 pub enum SdpBandwidth {
47 As(u32),
48 Ct(u32),
49 Tias(u32),
50 Unknown(String, u32),
51 }
52
53 impl fmt::Display for SdpBandwidth {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result54 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55 let (tp_string, value) = match *self {
56 SdpBandwidth::As(ref x) => ("AS", x),
57 SdpBandwidth::Ct(ref x) => ("CT", x),
58 SdpBandwidth::Tias(ref x) => ("TIAS", x),
59 SdpBandwidth::Unknown(ref tp, ref x) => (&tp[..], x),
60 };
61 write!(f, "{tp}:{val}", tp = tp_string, val = value)
62 }
63 }
64
65 /*
66 * RFC4566
67 * connection-field = [%x63 "=" nettype SP addrtype SP
68 * connection-address CRLF]
69 */
70 #[derive(Clone)]
71 #[cfg_attr(feature = "serialize", derive(Serialize))]
72 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
73 pub struct SdpConnection {
74 pub address: ExplicitlyTypedAddress,
75 pub ttl: Option<u8>,
76 pub amount: Option<u32>,
77 }
78
79 impl fmt::Display for SdpConnection {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 self.address.fmt(f)?;
82 write_option_string!(f, "/{}", self.ttl)?;
83 write_option_string!(f, "/{}", self.amount)
84 }
85 }
86
87 impl AnonymizingClone for SdpConnection {
masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self88 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
89 let mut masked = self.clone();
90 masked.address = anon.mask_typed_address(&self.address);
91 masked
92 }
93 }
94
95 /*
96 * RFC4566
97 * origin-field = %x6f "=" username SP sess-id SP sess-version SP
98 * nettype SP addrtype SP unicast-address CRLF
99 */
100 #[derive(Clone)]
101 #[cfg_attr(feature = "serialize", derive(Serialize))]
102 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
103 pub struct SdpOrigin {
104 pub username: String,
105 pub session_id: u64,
106 pub session_version: u64,
107 pub unicast_addr: ExplicitlyTypedAddress,
108 }
109
110 impl fmt::Display for SdpOrigin {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 write!(
113 f,
114 "{username} {sess_id} {sess_vers} {unicast_addr}",
115 username = self.username,
116 sess_id = self.session_id,
117 sess_vers = self.session_version,
118 unicast_addr = self.unicast_addr
119 )
120 }
121 }
122
123 impl AnonymizingClone for SdpOrigin {
masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self124 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
125 let mut masked = self.clone();
126 masked.username = anon.mask_origin_user(&self.username);
127 masked.unicast_addr = anon.mask_typed_address(&masked.unicast_addr);
128 masked
129 }
130 }
131
132 /*
133 * RFC4566
134 * time-fields = 1*( %x74 "=" start-time SP stop-time
135 * *(CRLF repeat-fields) CRLF)
136 * [zone-adjustments CRLF]
137 */
138 #[derive(Clone)]
139 #[cfg_attr(feature = "serialize", derive(Serialize))]
140 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
141 pub struct SdpTiming {
142 pub start: u64,
143 pub stop: u64,
144 }
145
146 impl fmt::Display for SdpTiming {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 write!(f, "{start} {stop}", start = self.start, stop = self.stop)
149 }
150 }
151
152 #[cfg_attr(feature = "serialize", derive(Serialize))]
153 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
154 pub enum SdpType {
155 // Note: Email, Information, Key, Phone, Repeat, Uri and Zone are left out
156 // on purposes as we don't want to support them.
157 Attribute(SdpAttribute),
158 Bandwidth(SdpBandwidth),
159 Connection(SdpConnection),
160 Media(SdpMediaLine),
161 Origin(SdpOrigin),
162 Session(String),
163 Timing(SdpTiming),
164 Version(u64),
165 }
166
167 #[cfg_attr(feature = "serialize", derive(Serialize))]
168 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
169 pub struct SdpLine {
170 pub line_number: usize,
171 pub sdp_type: SdpType,
172 pub text: String,
173 }
174
175 /*
176 * RFC4566
177 * ; SDP Syntax
178 * session-description = proto-version
179 * origin-field
180 * session-name-field
181 * information-field
182 * uri-field
183 * email-fields
184 * phone-fields
185 * connection-field
186 * bandwidth-fields
187 * time-fields
188 * key-field
189 * attribute-fields
190 * media-descriptions
191 */
192 #[derive(Clone)]
193 #[cfg_attr(feature = "serialize", derive(Serialize))]
194 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
195 pub struct SdpSession {
196 pub version: u64,
197 pub origin: SdpOrigin,
198 pub session: Option<String>,
199 pub connection: Option<SdpConnection>,
200 pub bandwidth: Vec<SdpBandwidth>,
201 pub timing: Option<SdpTiming>,
202 pub attribute: Vec<SdpAttribute>,
203 pub media: Vec<SdpMedia>,
204 pub warnings: Vec<SdpParserError>, // unsupported values:
205 // information: Option<String>,
206 // uri: Option<String>,
207 // email: Option<String>,
208 // phone: Option<String>,
209 // repeat: Option<String>,
210 // zone: Option<String>,
211 // key: Option<String>
212 }
213
214 impl fmt::Display for SdpSession {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result215 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216 write!(
217 f,
218 "v={version}\r\n\
219 o={origin}\r\n\
220 s={session}\r\n\
221 {timing}\
222 {bandwidth}\
223 {connection}\
224 {session_attributes}\
225 {media_sections}",
226 version = self.version,
227 origin = self.origin,
228 session = self.get_session_text(),
229 timing = option_to_string!("t={}\r\n", self.timing),
230 bandwidth = maybe_vector_to_string!("b={}\r\n", self.bandwidth, "\r\nb="),
231 connection = option_to_string!("c={}\r\n", self.connection),
232 session_attributes = maybe_vector_to_string!("a={}\r\n", self.attribute, "\r\na="),
233 media_sections = maybe_vector_to_string!("{}", self.media, "\r\n")
234 )
235 }
236 }
237
238 impl SdpSession {
new(version: u64, origin: SdpOrigin, session: String) -> SdpSession239 pub fn new(version: u64, origin: SdpOrigin, session: String) -> SdpSession {
240 let session = match session.trim() {
241 s if !s.is_empty() => Some(s.to_owned()),
242 _ => None,
243 };
244 SdpSession {
245 version,
246 origin,
247 session,
248 connection: None,
249 bandwidth: Vec::new(),
250 timing: None,
251 attribute: Vec::new(),
252 media: Vec::new(),
253 warnings: Vec::new(),
254 }
255 }
256
get_version(&self) -> u64257 pub fn get_version(&self) -> u64 {
258 self.version
259 }
260
get_origin(&self) -> &SdpOrigin261 pub fn get_origin(&self) -> &SdpOrigin {
262 &self.origin
263 }
264
get_session(&self) -> &Option<String>265 pub fn get_session(&self) -> &Option<String> {
266 &self.session
267 }
268
get_session_text(&self) -> &str269 pub fn get_session_text(&self) -> &str {
270 if let Some(text) = &self.session {
271 text.as_str()
272 } else {
273 " "
274 }
275 }
get_connection(&self) -> &Option<SdpConnection>276 pub fn get_connection(&self) -> &Option<SdpConnection> {
277 &self.connection
278 }
279
set_connection(&mut self, c: SdpConnection)280 pub fn set_connection(&mut self, c: SdpConnection) {
281 self.connection = Some(c)
282 }
283
add_bandwidth(&mut self, b: SdpBandwidth)284 pub fn add_bandwidth(&mut self, b: SdpBandwidth) {
285 self.bandwidth.push(b)
286 }
287
set_timing(&mut self, t: SdpTiming)288 pub fn set_timing(&mut self, t: SdpTiming) {
289 self.timing = Some(t)
290 }
291
add_attribute(&mut self, a: SdpAttribute) -> Result<(), SdpParserInternalError>292 pub fn add_attribute(&mut self, a: SdpAttribute) -> Result<(), SdpParserInternalError> {
293 if !a.allowed_at_session_level() {
294 return Err(SdpParserInternalError::Generic(format!(
295 "{} not allowed at session level",
296 a
297 )));
298 };
299 self.attribute.push(a);
300 Ok(())
301 }
302
extend_media(&mut self, v: Vec<SdpMedia>)303 pub fn extend_media(&mut self, v: Vec<SdpMedia>) {
304 self.media.extend(v)
305 }
306
parse_session_vector(&mut self, lines: &mut Vec<SdpLine>) -> Result<(), SdpParserError>307 pub fn parse_session_vector(&mut self, lines: &mut Vec<SdpLine>) -> Result<(), SdpParserError> {
308 while !lines.is_empty() {
309 let line = lines.remove(0);
310 match line.sdp_type {
311 SdpType::Attribute(a) => {
312 let _line_number = line.line_number;
313 self.add_attribute(a).map_err(|e: SdpParserInternalError| {
314 SdpParserError::Sequence {
315 message: format!("{}", e),
316 line_number: _line_number,
317 }
318 })?
319 }
320 SdpType::Bandwidth(b) => self.add_bandwidth(b),
321 SdpType::Timing(t) => self.set_timing(t),
322 SdpType::Connection(c) => self.set_connection(c),
323
324 SdpType::Origin(_) | SdpType::Session(_) | SdpType::Version(_) => {
325 return Err(SdpParserError::Sequence {
326 message: "version, origin or session at wrong level".to_string(),
327 line_number: line.line_number,
328 });
329 }
330 SdpType::Media(_) => {
331 return Err(SdpParserError::Sequence {
332 message: "media line not allowed in session parser".to_string(),
333 line_number: line.line_number,
334 });
335 }
336 }
337 }
338 Ok(())
339 }
340
get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute>341 pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> {
342 self.attribute
343 .iter()
344 .find(|a| SdpAttributeType::from(*a) == t)
345 }
346
add_media( &mut self, media_type: SdpMediaValue, direction: SdpAttribute, port: u32, protocol: SdpProtocolValue, addr: ExplicitlyTypedAddress, ) -> Result<(), SdpParserInternalError>347 pub fn add_media(
348 &mut self,
349 media_type: SdpMediaValue,
350 direction: SdpAttribute,
351 port: u32,
352 protocol: SdpProtocolValue,
353 addr: ExplicitlyTypedAddress,
354 ) -> Result<(), SdpParserInternalError> {
355 let mut media = SdpMedia::new(SdpMediaLine {
356 media: media_type,
357 port,
358 port_count: 1,
359 proto: protocol,
360 formats: SdpFormatList::Integers(Vec::new()),
361 });
362
363 media.add_attribute(direction)?;
364
365 media.set_connection(SdpConnection {
366 address: addr,
367 ttl: None,
368 amount: None,
369 });
370
371 self.media.push(media);
372
373 Ok(())
374 }
375 }
376
377 impl AnonymizingClone for SdpSession {
masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self378 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
379 let mut masked: SdpSession = SdpSession {
380 version: self.version,
381 session: self.session.clone(),
382 origin: self.origin.masked_clone(anon),
383 connection: self.connection.clone(),
384 timing: self.timing.clone(),
385 bandwidth: self.bandwidth.clone(),
386 attribute: Vec::new(),
387 media: Vec::new(),
388 warnings: Vec::new(),
389 };
390 masked.origin = self.origin.masked_clone(anon);
391 masked.connection = masked.connection.map(|con| con.masked_clone(anon));
392 for i in &self.attribute {
393 masked.attribute.push(i.masked_clone(anon));
394 }
395 masked
396 }
397 }
398
parse_session(value: &str) -> Result<SdpType, SdpParserInternalError>399 fn parse_session(value: &str) -> Result<SdpType, SdpParserInternalError> {
400 trace!("session: {}", value);
401 Ok(SdpType::Session(String::from(value)))
402 }
403
parse_version(value: &str) -> Result<SdpType, SdpParserInternalError>404 fn parse_version(value: &str) -> Result<SdpType, SdpParserInternalError> {
405 let ver = value.parse::<u64>()?;
406 if ver != 0 {
407 return Err(SdpParserInternalError::Generic(format!(
408 "version type contains unsupported value {}",
409 ver
410 )));
411 };
412 trace!("version: {}", ver);
413 Ok(SdpType::Version(ver))
414 }
415
parse_origin(value: &str) -> Result<SdpType, SdpParserInternalError>416 fn parse_origin(value: &str) -> Result<SdpType, SdpParserInternalError> {
417 let mut tokens = value.split_whitespace();
418 let username = match tokens.next() {
419 None => {
420 return Err(SdpParserInternalError::Generic(
421 "Origin type is missing username token".to_string(),
422 ));
423 }
424 Some(x) => x,
425 };
426 let session_id = match tokens.next() {
427 None => {
428 return Err(SdpParserInternalError::Generic(
429 "Origin type is missing session ID token".to_string(),
430 ));
431 }
432 Some(x) => x.parse::<u64>()?,
433 };
434 let session_version = match tokens.next() {
435 None => {
436 return Err(SdpParserInternalError::Generic(
437 "Origin type is missing session version token".to_string(),
438 ));
439 }
440 Some(x) => x.parse::<u64>()?,
441 };
442 match tokens.next() {
443 None => {
444 return Err(SdpParserInternalError::Generic(
445 "Origin type is missing network type token".to_string(),
446 ));
447 }
448 Some(x) => parse_network_type(x)?,
449 };
450 let addrtype = match tokens.next() {
451 None => {
452 return Err(SdpParserInternalError::Generic(
453 "Origin type is missing address type token".to_string(),
454 ));
455 }
456 Some(x) => parse_address_type(x)?,
457 };
458 let unicast_addr = match tokens.next() {
459 None => {
460 return Err(SdpParserInternalError::Generic(
461 "Origin type is missing IP address token".to_string(),
462 ));
463 }
464 Some(x) => ExplicitlyTypedAddress::try_from((addrtype, x))?,
465 };
466 if addrtype != unicast_addr.address_type() {
467 return Err(SdpParserInternalError::Generic(
468 "Origin addrtype does not match address.".to_string(),
469 ));
470 }
471 let o = SdpOrigin {
472 username: String::from(username),
473 session_id,
474 session_version,
475 unicast_addr,
476 };
477 trace!("origin: {}", o);
478 Ok(SdpType::Origin(o))
479 }
480
parse_connection(value: &str) -> Result<SdpType, SdpParserInternalError>481 fn parse_connection(value: &str) -> Result<SdpType, SdpParserInternalError> {
482 let cv: Vec<&str> = value.split_whitespace().collect();
483 if cv.len() != 3 {
484 return Err(SdpParserInternalError::Generic(
485 "connection attribute must have three tokens".to_string(),
486 ));
487 }
488 parse_network_type(cv[0])?;
489 let addrtype = parse_address_type(cv[1])?;
490 let mut ttl = None;
491 let mut amount = None;
492 let mut addr_token = cv[2];
493 if addr_token.find('/') != None {
494 let addr_tokens: Vec<&str> = addr_token.split('/').collect();
495 if addr_tokens.len() >= 3 {
496 amount = Some(addr_tokens[2].parse::<u32>()?);
497 }
498 ttl = Some(addr_tokens[1].parse::<u8>()?);
499 addr_token = addr_tokens[0];
500 }
501 let address = ExplicitlyTypedAddress::try_from((addrtype, addr_token))?;
502 let c = SdpConnection {
503 address,
504 ttl,
505 amount,
506 };
507 trace!("connection: {}", c);
508 Ok(SdpType::Connection(c))
509 }
510
parse_bandwidth(value: &str) -> Result<SdpType, SdpParserInternalError>511 fn parse_bandwidth(value: &str) -> Result<SdpType, SdpParserInternalError> {
512 let bv: Vec<&str> = value.split(':').collect();
513 if bv.len() != 2 {
514 return Err(SdpParserInternalError::Generic(
515 "bandwidth attribute must have two tokens".to_string(),
516 ));
517 }
518 let bandwidth = bv[1].parse::<u32>()?;
519 let bw = match bv[0].to_uppercase().as_ref() {
520 "AS" => SdpBandwidth::As(bandwidth),
521 "CT" => SdpBandwidth::Ct(bandwidth),
522 "TIAS" => SdpBandwidth::Tias(bandwidth),
523 _ => SdpBandwidth::Unknown(String::from(bv[0]), bandwidth),
524 };
525 trace!("bandwidth: {}", bw);
526 Ok(SdpType::Bandwidth(bw))
527 }
528
parse_timing(value: &str) -> Result<SdpType, SdpParserInternalError>529 fn parse_timing(value: &str) -> Result<SdpType, SdpParserInternalError> {
530 let tv: Vec<&str> = value.split_whitespace().collect();
531 if tv.len() != 2 {
532 return Err(SdpParserInternalError::Generic(
533 "timing attribute must have two tokens".to_string(),
534 ));
535 }
536 let start = tv[0].parse::<u64>()?;
537 let stop = tv[1].parse::<u64>()?;
538 let t = SdpTiming { start, stop };
539 trace!("timing: {}", t);
540 Ok(SdpType::Timing(t))
541 }
542
parse_sdp_line(line: &str, line_number: usize) -> Result<SdpLine, SdpParserError>543 fn parse_sdp_line(line: &str, line_number: usize) -> Result<SdpLine, SdpParserError> {
544 if line.find('=') == None {
545 return Err(SdpParserError::Line {
546 error: SdpParserInternalError::Generic("missing = character in line".to_string()),
547 line: line.to_string(),
548 line_number,
549 });
550 }
551 let mut splitted_line = line.splitn(2, '=');
552 let line_type = match splitted_line.next() {
553 None => {
554 return Err(SdpParserError::Line {
555 error: SdpParserInternalError::Generic("missing type".to_string()),
556 line: line.to_string(),
557 line_number,
558 });
559 }
560 Some(t) => {
561 let trimmed = t.trim();
562 if trimmed.len() > 1 {
563 return Err(SdpParserError::Line {
564 error: SdpParserInternalError::Generic("type too long".to_string()),
565 line: line.to_string(),
566 line_number,
567 });
568 }
569 if trimmed.is_empty() {
570 return Err(SdpParserError::Line {
571 error: SdpParserInternalError::Generic("type is empty".to_string()),
572 line: line.to_string(),
573 line_number,
574 });
575 }
576 trimmed.to_lowercase()
577 }
578 };
579 let (line_value, untrimmed_line_value) = match splitted_line.next() {
580 None => {
581 return Err(SdpParserError::Line {
582 error: SdpParserInternalError::Generic("missing value".to_string()),
583 line: line.to_string(),
584 line_number,
585 });
586 }
587 Some(v) => {
588 let trimmed = v.trim();
589 // For compatibility with sites that don't adhere to "s=-" for no session ID
590 if trimmed.is_empty() && line_type.as_str() != "s" {
591 return Err(SdpParserError::Line {
592 error: SdpParserInternalError::Generic("value is empty".to_string()),
593 line: line.to_string(),
594 line_number,
595 });
596 }
597 (trimmed, v)
598 }
599 };
600 match line_type.as_ref() {
601 "a" => parse_attribute(line_value),
602 "b" => parse_bandwidth(line_value),
603 "c" => parse_connection(line_value),
604 "e" => Err(SdpParserInternalError::Generic(format!(
605 "unsupported type email: {}",
606 line_value
607 ))),
608 "i" => Err(SdpParserInternalError::Generic(format!(
609 "unsupported type information: {}",
610 line_value
611 ))),
612 "k" => Err(SdpParserInternalError::Generic(format!(
613 "unsupported insecure key exchange: {}",
614 line_value
615 ))),
616 "m" => parse_media(line_value),
617 "o" => parse_origin(line_value),
618 "p" => Err(SdpParserInternalError::Generic(format!(
619 "unsupported type phone: {}",
620 line_value
621 ))),
622 "r" => Err(SdpParserInternalError::Generic(format!(
623 "unsupported type repeat: {}",
624 line_value
625 ))),
626 "s" => parse_session(untrimmed_line_value),
627 "t" => parse_timing(line_value),
628 "u" => Err(SdpParserInternalError::Generic(format!(
629 "unsupported type uri: {}",
630 line_value
631 ))),
632 "v" => parse_version(line_value),
633 "z" => Err(SdpParserInternalError::Generic(format!(
634 "unsupported type zone: {}",
635 line_value
636 ))),
637 _ => Err(SdpParserInternalError::Generic(
638 "unknown sdp type".to_string(),
639 )),
640 }
641 .map(|sdp_type| SdpLine {
642 line_number,
643 sdp_type,
644 text: line.to_owned(),
645 })
646 .map_err(|e| match e {
647 SdpParserInternalError::UnknownAddressType(..)
648 | SdpParserInternalError::AddressTypeMismatch { .. }
649 | SdpParserInternalError::Generic(..)
650 | SdpParserInternalError::Integer(..)
651 | SdpParserInternalError::Float(..)
652 | SdpParserInternalError::Domain(..)
653 | SdpParserInternalError::IpAddress(..) => SdpParserError::Line {
654 error: e,
655 line: line.to_string(),
656 line_number,
657 },
658 SdpParserInternalError::Unsupported(..) => SdpParserError::Unsupported {
659 error: e,
660 line: line.to_string(),
661 line_number,
662 },
663 })
664 }
665
sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError>666 fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> {
667 let make_seq_error = |x: &str| SdpParserError::Sequence {
668 message: x.to_string(),
669 line_number: 0,
670 };
671
672 if session.timing.is_none() {
673 return Err(make_seq_error("Missing timing type at session level"));
674 }
675 // Checks that all media have connections if there is no top level
676 // This explicitly allows for zero connection lines if there are no media
677 // sections for interoperability reasons.
678 let media_cons = &session.media.iter().all(|m| m.get_connection().is_some());
679 if !media_cons && session.get_connection().is_none() {
680 return Err(make_seq_error(
681 "Without connection type at session level all media sections must have connection types",
682 ));
683 }
684
685 // Check that extmaps are not defined on session and media level
686 if session.get_attribute(SdpAttributeType::Extmap).is_some() {
687 for msection in &session.media {
688 if msection.get_attribute(SdpAttributeType::Extmap).is_some() {
689 return Err(make_seq_error(
690 "Extmap can't be define at session and media level",
691 ));
692 }
693 }
694 }
695
696 for msection in &session.media {
697 if msection.get_attribute(SdpAttributeType::Sendonly).is_some() {
698 if let Some(&SdpAttribute::Simulcast(ref x)) =
699 msection.get_attribute(SdpAttributeType::Simulcast)
700 {
701 if !x.receive.is_empty() {
702 return Err(make_seq_error(
703 "Simulcast can't define receive parameters for sendonly",
704 ));
705 }
706 }
707 }
708 if msection.get_attribute(SdpAttributeType::Recvonly).is_some() {
709 if let Some(&SdpAttribute::Simulcast(ref x)) =
710 msection.get_attribute(SdpAttributeType::Simulcast)
711 {
712 if !x.send.is_empty() {
713 return Err(make_seq_error(
714 "Simulcast can't define send parameters for recvonly",
715 ));
716 }
717 }
718 }
719
720 let rids: Vec<&SdpAttributeRid> = msection
721 .get_attributes()
722 .iter()
723 .filter_map(|attr| match *attr {
724 SdpAttribute::Rid(ref rid) => Some(rid),
725 _ => None,
726 })
727 .collect();
728 let recv_rids: Vec<&str> = rids
729 .iter()
730 .filter_map(|rid| match rid.direction {
731 SdpSingleDirection::Recv => Some(rid.id.as_str()),
732 _ => None,
733 })
734 .collect();
735 let send_rids: Vec<&str> = rids
736 .iter()
737 .filter_map(|rid| match rid.direction {
738 SdpSingleDirection::Send => Some(rid.id.as_str()),
739 _ => None,
740 })
741 .collect();
742
743 for rid_format in rids.iter().flat_map(|rid| &rid.formats) {
744 match *msection.get_formats() {
745 SdpFormatList::Integers(ref int_fmt) => {
746 if !int_fmt.contains(&(u32::from(*rid_format))) {
747 return Err(make_seq_error(
748 "Rid pts must be declared in the media section",
749 ));
750 }
751 }
752 SdpFormatList::Strings(ref str_fmt) => {
753 if !str_fmt.contains(&rid_format.to_string()) {
754 return Err(make_seq_error(
755 "Rid pts must be declared in the media section",
756 ));
757 }
758 }
759 }
760 }
761
762 if let Some(&SdpAttribute::Simulcast(ref simulcast)) =
763 msection.get_attribute(SdpAttributeType::Simulcast)
764 {
765 let check_defined_rids =
766 |simulcast_version_list: &Vec<SdpAttributeSimulcastVersion>,
767 rid_ids: &[&str]|
768 -> Result<(), SdpParserError> {
769 for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) {
770 if !rid_ids.contains(&simulcast_rid.id.as_str()) {
771 return Err(make_seq_error(
772 "Simulcast RIDs must be defined in any rid attribute",
773 ));
774 }
775 }
776 Ok(())
777 };
778
779 check_defined_rids(&simulcast.receive, &recv_rids)?;
780 check_defined_rids(&simulcast.send, &send_rids)?;
781 }
782 }
783
784 Ok(())
785 }
786
parse_sdp_vector(lines: &mut Vec<SdpLine>) -> Result<SdpSession, SdpParserError>787 fn parse_sdp_vector(lines: &mut Vec<SdpLine>) -> Result<SdpSession, SdpParserError> {
788 if lines.len() < 4 {
789 return Err(SdpParserError::Sequence {
790 message: "SDP neeeds at least 4 lines".to_string(),
791 line_number: 0,
792 });
793 }
794
795 let version = match lines.remove(0).sdp_type {
796 SdpType::Version(v) => v,
797 _ => {
798 return Err(SdpParserError::Sequence {
799 message: "first line needs to be version number".to_string(),
800 line_number: 0,
801 });
802 }
803 };
804 let origin = match lines.remove(0).sdp_type {
805 SdpType::Origin(v) => v,
806 _ => {
807 return Err(SdpParserError::Sequence {
808 message: "second line needs to be origin".to_string(),
809 line_number: 1,
810 });
811 }
812 };
813 let session = match lines.remove(0).sdp_type {
814 SdpType::Session(v) => v,
815 _ => {
816 return Err(SdpParserError::Sequence {
817 message: "third line needs to be session".to_string(),
818 line_number: 2,
819 });
820 }
821 };
822 let mut sdp_session = SdpSession::new(version, origin, session);
823
824 let _media_pos = lines
825 .iter()
826 .position(|ref l| matches!(l.sdp_type, SdpType::Media(_)));
827
828 match _media_pos {
829 Some(p) => {
830 let mut media: Vec<_> = lines.drain(p..).collect();
831 sdp_session.parse_session_vector(lines)?;
832 sdp_session.extend_media(parse_media_vector(&mut media)?);
833 }
834 None => sdp_session.parse_session_vector(lines)?,
835 };
836
837 sanity_check_sdp_session(&sdp_session)?;
838 Ok(sdp_session)
839 }
840
parse_sdp(sdp: &str, fail_on_warning: bool) -> Result<SdpSession, SdpParserError>841 pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result<SdpSession, SdpParserError> {
842 if sdp.is_empty() {
843 return Err(SdpParserError::Line {
844 error: SdpParserInternalError::Generic("empty SDP".to_string()),
845 line: sdp.to_string(),
846 line_number: 0,
847 });
848 }
849 // see test_parse_sdp_minimal_sdp_successfully
850 if sdp.len() < 51 {
851 return Err(SdpParserError::Line {
852 error: SdpParserInternalError::Generic("string too short to be valid SDP".to_string()),
853 line: sdp.to_string(),
854 line_number: 0,
855 });
856 }
857 let lines = sdp.lines();
858 let mut errors: Vec<SdpParserError> = Vec::new();
859 let mut warnings: Vec<SdpParserError> = Vec::new();
860 let mut sdp_lines: Vec<SdpLine> = Vec::new();
861 for (line_number, line) in lines.enumerate() {
862 let stripped_line = line.trim();
863 if stripped_line.is_empty() {
864 continue;
865 }
866 match parse_sdp_line(line, line_number) {
867 Ok(n) => {
868 sdp_lines.push(n);
869 }
870 Err(e) => {
871 match e {
872 // TODO is this really a good way to accomplish this?
873 SdpParserError::Line {
874 error,
875 line,
876 line_number,
877 } => errors.push(SdpParserError::Line {
878 error,
879 line,
880 line_number,
881 }),
882 SdpParserError::Unsupported {
883 error,
884 line,
885 line_number,
886 } => {
887 warnings.push(SdpParserError::Unsupported {
888 error,
889 line,
890 line_number,
891 });
892 }
893 SdpParserError::Sequence {
894 message,
895 line_number,
896 } => errors.push(SdpParserError::Sequence {
897 message,
898 line_number,
899 }),
900 }
901 }
902 };
903 }
904
905 if fail_on_warning && (!warnings.is_empty()) {
906 return Err(warnings.remove(0));
907 }
908
909 // We just return the last of the errors here
910 if let Some(e) = errors.pop() {
911 return Err(e);
912 };
913
914 let mut session = parse_sdp_vector(&mut sdp_lines)?;
915 session.warnings = warnings;
916
917 for warning in &session.warnings {
918 warn!("Warning: {}", &warning);
919 }
920
921 Ok(session)
922 }
923
924 #[cfg(test)]
925 mod tests {
926 extern crate url;
927 use super::*;
928 use address::{Address, AddressType};
929 use anonymizer::ToBytesVec;
930 use media_type::create_dummy_media_section;
931 use std::net::IpAddr;
932 use std::net::Ipv4Addr;
933
create_dummy_sdp_session() -> SdpSession934 fn create_dummy_sdp_session() -> SdpSession {
935 let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0");
936 assert!(origin.is_ok());
937 let connection = parse_connection("IN IP4 198.51.100.7");
938 assert!(connection.is_ok());
939 let mut sdp_session;
940 if let SdpType::Origin(o) = origin.unwrap() {
941 sdp_session = SdpSession::new(0, o, "-".to_string());
942
943 if let Ok(SdpType::Connection(c)) = connection {
944 sdp_session.connection = Some(c);
945 } else {
946 unreachable!();
947 }
948 } else {
949 unreachable!();
950 }
951 sdp_session
952 }
953
954 #[test]
test_session_works() -> Result<(), SdpParserInternalError>955 fn test_session_works() -> Result<(), SdpParserInternalError> {
956 parse_session("topic")?;
957 Ok(())
958 }
959
960 #[test]
test_version_works() -> Result<(), SdpParserInternalError>961 fn test_version_works() -> Result<(), SdpParserInternalError> {
962 parse_version("0")?;
963 Ok(())
964 }
965
966 #[test]
test_version_unsupported_input()967 fn test_version_unsupported_input() {
968 assert!(parse_version("1").is_err());
969 assert!(parse_version("11").is_err());
970 assert!(parse_version("a").is_err());
971 }
972
973 #[test]
test_origin_works() -> Result<(), SdpParserInternalError>974 fn test_origin_works() -> Result<(), SdpParserInternalError> {
975 parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0")?;
976 parse_origin("mozilla 506705521068071134 0 IN IP6 2001:db8::1")?;
977 Ok(())
978 }
979
980 #[test]
test_origin_missing_username()981 fn test_origin_missing_username() {
982 assert!(parse_origin("").is_err());
983 }
984
985 #[test]
test_origin_missing_session_id()986 fn test_origin_missing_session_id() {
987 assert!(parse_origin("mozilla ").is_err());
988 }
989
990 #[test]
test_origin_missing_session_version()991 fn test_origin_missing_session_version() {
992 assert!(parse_origin("mozilla 506705521068071134 ").is_err());
993 }
994
995 #[test]
test_origin_missing_nettype()996 fn test_origin_missing_nettype() {
997 assert!(parse_origin("mozilla 506705521068071134 0 ").is_err());
998 }
999
1000 #[test]
test_origin_unsupported_nettype()1001 fn test_origin_unsupported_nettype() {
1002 assert!(parse_origin("mozilla 506705521068071134 0 UNSUPPORTED IP4 0.0.0.0").is_err());
1003 }
1004
1005 #[test]
test_origin_missing_addtype()1006 fn test_origin_missing_addtype() {
1007 assert!(parse_origin("mozilla 506705521068071134 0 IN ").is_err());
1008 }
1009
1010 #[test]
test_origin_missing_ip_addr()1011 fn test_origin_missing_ip_addr() {
1012 assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 ").is_err());
1013 }
1014
1015 #[test]
test_origin_unsupported_addrtpe()1016 fn test_origin_unsupported_addrtpe() {
1017 assert!(parse_origin("mozilla 506705521068071134 0 IN IP1 0.0.0.0").is_err());
1018 }
1019
1020 #[test]
test_origin_invalid_ip_addr()1021 fn test_origin_invalid_ip_addr() {
1022 assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 1.1.1.256").is_err());
1023 assert!(parse_origin("mozilla 506705521068071134 0 IN IP6 ::g").is_err());
1024 }
1025
1026 #[test]
test_origin_addr_type_mismatch()1027 fn test_origin_addr_type_mismatch() {
1028 assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 ::1").is_err());
1029 }
1030
1031 #[test]
connection_works() -> Result<(), SdpParserInternalError>1032 fn connection_works() -> Result<(), SdpParserInternalError> {
1033 parse_connection("IN IP4 127.0.0.1")?;
1034 parse_connection("IN IP4 127.0.0.1/10/10")?;
1035 parse_connection("IN IP6 ::1")?;
1036 parse_connection("IN IP6 ::1/1/1")?;
1037 Ok(())
1038 }
1039
1040 #[test]
connection_lots_of_whitespace() -> Result<(), SdpParserInternalError>1041 fn connection_lots_of_whitespace() -> Result<(), SdpParserInternalError> {
1042 parse_connection("IN IP4 127.0.0.1")?;
1043 Ok(())
1044 }
1045
1046 #[test]
connection_wrong_amount_of_tokens()1047 fn connection_wrong_amount_of_tokens() {
1048 assert!(parse_connection("IN IP4").is_err());
1049 assert!(parse_connection("IN IP4 0.0.0.0 foobar").is_err());
1050 }
1051
1052 #[test]
connection_unsupported_nettype()1053 fn connection_unsupported_nettype() {
1054 assert!(parse_connection("UNSUPPORTED IP4 0.0.0.0").is_err());
1055 }
1056
1057 #[test]
connection_unsupported_addrtpe()1058 fn connection_unsupported_addrtpe() {
1059 assert!(parse_connection("IN IP1 0.0.0.0").is_err());
1060 }
1061
1062 #[test]
connection_broken_ip_addr()1063 fn connection_broken_ip_addr() {
1064 assert!(parse_connection("IN IP4 1.1.1.256").is_err());
1065 assert!(parse_connection("IN IP6 ::g").is_err());
1066 }
1067
1068 #[test]
connection_addr_type_mismatch()1069 fn connection_addr_type_mismatch() {
1070 assert!(parse_connection("IN IP4 ::1").is_err());
1071 }
1072
1073 #[test]
bandwidth_works() -> Result<(), SdpParserInternalError>1074 fn bandwidth_works() -> Result<(), SdpParserInternalError> {
1075 parse_bandwidth("AS:1")?;
1076 parse_bandwidth("CT:123")?;
1077 parse_bandwidth("TIAS:12345")?;
1078 Ok(())
1079 }
1080
1081 #[test]
bandwidth_wrong_amount_of_tokens()1082 fn bandwidth_wrong_amount_of_tokens() {
1083 assert!(parse_bandwidth("TIAS").is_err());
1084 assert!(parse_bandwidth("TIAS:12345:xyz").is_err());
1085 }
1086
1087 #[test]
bandwidth_unsupported_type() -> Result<(), SdpParserInternalError>1088 fn bandwidth_unsupported_type() -> Result<(), SdpParserInternalError> {
1089 parse_bandwidth("UNSUPPORTED:12345")?;
1090 Ok(())
1091 }
1092
1093 #[test]
test_timing_works() -> Result<(), SdpParserInternalError>1094 fn test_timing_works() -> Result<(), SdpParserInternalError> {
1095 parse_timing("0 0")?;
1096 Ok(())
1097 }
1098
1099 #[test]
test_timing_non_numeric_tokens()1100 fn test_timing_non_numeric_tokens() {
1101 assert!(parse_timing("a 0").is_err());
1102 assert!(parse_timing("0 a").is_err());
1103 }
1104
1105 #[test]
test_timing_wrong_amount_of_tokens()1106 fn test_timing_wrong_amount_of_tokens() {
1107 assert!(parse_timing("0").is_err());
1108 assert!(parse_timing("0 0 0").is_err());
1109 }
1110
1111 #[test]
test_parse_sdp_line_works() -> Result<(), SdpParserError>1112 fn test_parse_sdp_line_works() -> Result<(), SdpParserError> {
1113 parse_sdp_line("v=0", 0)?;
1114 parse_sdp_line("s=somesession", 0)?;
1115 Ok(())
1116 }
1117
1118 #[test]
test_parse_sdp_line_empty_line()1119 fn test_parse_sdp_line_empty_line() {
1120 assert!(parse_sdp_line("", 0).is_err());
1121 }
1122
1123 #[test]
test_parse_sdp_line_unsupported_types()1124 fn test_parse_sdp_line_unsupported_types() {
1125 assert!(parse_sdp_line("e=foobar", 0).is_err());
1126 assert!(parse_sdp_line("i=foobar", 0).is_err());
1127 assert!(parse_sdp_line("k=foobar", 0).is_err());
1128 assert!(parse_sdp_line("p=foobar", 0).is_err());
1129 assert!(parse_sdp_line("r=foobar", 0).is_err());
1130 assert!(parse_sdp_line("u=foobar", 0).is_err());
1131 assert!(parse_sdp_line("z=foobar", 0).is_err());
1132 }
1133
1134 #[test]
test_parse_sdp_line_unknown_key()1135 fn test_parse_sdp_line_unknown_key() {
1136 assert!(parse_sdp_line("y=foobar", 0).is_err());
1137 }
1138
1139 #[test]
test_parse_sdp_line_too_long_type()1140 fn test_parse_sdp_line_too_long_type() {
1141 assert!(parse_sdp_line("ab=foobar", 0).is_err());
1142 }
1143
1144 #[test]
test_parse_sdp_line_without_equal()1145 fn test_parse_sdp_line_without_equal() {
1146 assert!(parse_sdp_line("abcd", 0).is_err());
1147 assert!(parse_sdp_line("ab cd", 0).is_err());
1148 }
1149
1150 #[test]
test_parse_sdp_line_empty_value()1151 fn test_parse_sdp_line_empty_value() {
1152 assert!(parse_sdp_line("v=", 0).is_err());
1153 assert!(parse_sdp_line("o=", 0).is_err());
1154 }
1155
1156 #[test]
test_parse_sdp_line_empty_name()1157 fn test_parse_sdp_line_empty_name() {
1158 assert!(parse_sdp_line("=abc", 0).is_err());
1159 }
1160
1161 #[test]
test_parse_sdp_line_valid_a_line() -> Result<(), SdpParserError>1162 fn test_parse_sdp_line_valid_a_line() -> Result<(), SdpParserError> {
1163 parse_sdp_line("a=rtpmap:8 PCMA/8000", 0)?;
1164 Ok(())
1165 }
1166
1167 #[test]
test_parse_sdp_line_invalid_a_line()1168 fn test_parse_sdp_line_invalid_a_line() {
1169 assert!(parse_sdp_line("a=rtpmap:200 PCMA/8000", 0).is_err());
1170 }
1171
1172 #[test]
test_add_attribute() -> Result<(), SdpParserInternalError>1173 fn test_add_attribute() -> Result<(), SdpParserInternalError> {
1174 let mut sdp_session = create_dummy_sdp_session();
1175
1176 sdp_session.add_attribute(SdpAttribute::Sendrecv)?;
1177 assert!(sdp_session.add_attribute(SdpAttribute::BundleOnly).is_err());
1178 assert_eq!(sdp_session.attribute.len(), 1);
1179 Ok(())
1180 }
1181
1182 #[test]
test_sanity_check_sdp_session_timing() -> Result<(), SdpParserError>1183 fn test_sanity_check_sdp_session_timing() -> Result<(), SdpParserError> {
1184 let mut sdp_session = create_dummy_sdp_session();
1185 sdp_session.extend_media(vec![create_dummy_media_section()]);
1186
1187 assert!(sanity_check_sdp_session(&sdp_session).is_err());
1188
1189 let t = SdpTiming { start: 0, stop: 0 };
1190 sdp_session.set_timing(t);
1191
1192 sanity_check_sdp_session(&sdp_session)?;
1193 Ok(())
1194 }
1195
1196 #[test]
test_sanity_check_sdp_session_media() -> Result<(), SdpParserError>1197 fn test_sanity_check_sdp_session_media() -> Result<(), SdpParserError> {
1198 let mut sdp_session = create_dummy_sdp_session();
1199 let t = SdpTiming { start: 0, stop: 0 };
1200 sdp_session.set_timing(t);
1201
1202 sanity_check_sdp_session(&sdp_session)?;
1203
1204 sdp_session.extend_media(vec![create_dummy_media_section()]);
1205
1206 sanity_check_sdp_session(&sdp_session)?;
1207 Ok(())
1208 }
1209
1210 #[test]
test_sanity_check_sdp_connection() -> Result<(), SdpParserInternalError>1211 fn test_sanity_check_sdp_connection() -> Result<(), SdpParserInternalError> {
1212 let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0")?;
1213 let mut sdp_session;
1214 if let SdpType::Origin(o) = origin {
1215 sdp_session = SdpSession::new(0, o, "-".to_string());
1216 } else {
1217 unreachable!();
1218 }
1219 let t = SdpTiming { start: 0, stop: 0 };
1220 sdp_session.set_timing(t);
1221
1222 assert!(sanity_check_sdp_session(&sdp_session).is_ok());
1223
1224 // the dummy media section doesn't contain a connection
1225 sdp_session.extend_media(vec![create_dummy_media_section()]);
1226
1227 assert!(sanity_check_sdp_session(&sdp_session).is_err());
1228
1229 let connection = parse_connection("IN IP6 ::1")?;
1230 if let SdpType::Connection(c) = connection {
1231 sdp_session.connection = Some(c);
1232 } else {
1233 unreachable!();
1234 }
1235
1236 assert!(sanity_check_sdp_session(&sdp_session).is_ok());
1237
1238 let mut second_media = create_dummy_media_section();
1239 let mconnection = parse_connection("IN IP4 0.0.0.0")?;
1240 if let SdpType::Connection(c) = mconnection {
1241 second_media.set_connection(c);
1242 } else {
1243 unreachable!();
1244 }
1245 sdp_session.extend_media(vec![second_media]);
1246 assert!(sdp_session.media.len() == 2);
1247
1248 assert!(sanity_check_sdp_session(&sdp_session).is_ok());
1249 Ok(())
1250 }
1251
1252 #[test]
test_sanity_check_sdp_session_extmap() -> Result<(), SdpParserInternalError>1253 fn test_sanity_check_sdp_session_extmap() -> Result<(), SdpParserInternalError> {
1254 let mut sdp_session = create_dummy_sdp_session();
1255 let t = SdpTiming { start: 0, stop: 0 };
1256 sdp_session.set_timing(t);
1257 sdp_session.extend_media(vec![create_dummy_media_section()]);
1258
1259 let attribute =
1260 parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")?;
1261 if let SdpType::Attribute(a) = attribute {
1262 sdp_session.add_attribute(a)?;
1263 } else {
1264 unreachable!();
1265 }
1266 assert!(sdp_session
1267 .get_attribute(SdpAttributeType::Extmap)
1268 .is_some());
1269
1270 assert!(sanity_check_sdp_session(&sdp_session).is_ok());
1271
1272 let mut second_media = create_dummy_media_section();
1273 let mattribute =
1274 parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level")?;
1275 if let SdpType::Attribute(ma) = mattribute {
1276 second_media.add_attribute(ma)?;
1277 } else {
1278 unreachable!();
1279 }
1280 assert!(second_media
1281 .get_attribute(SdpAttributeType::Extmap)
1282 .is_some());
1283
1284 sdp_session.extend_media(vec![second_media]);
1285 assert!(sdp_session.media.len() == 2);
1286
1287 assert!(sanity_check_sdp_session(&sdp_session).is_err());
1288
1289 sdp_session.attribute = Vec::new();
1290
1291 assert!(sanity_check_sdp_session(&sdp_session).is_ok());
1292 Ok(())
1293 }
1294
1295 #[test]
test_sanity_check_sdp_session_simulcast() -> Result<(), SdpParserError>1296 fn test_sanity_check_sdp_session_simulcast() -> Result<(), SdpParserError> {
1297 let mut sdp_session = create_dummy_sdp_session();
1298 let t = SdpTiming { start: 0, stop: 0 };
1299 sdp_session.set_timing(t);
1300 sdp_session.extend_media(vec![create_dummy_media_section()]);
1301
1302 sanity_check_sdp_session(&sdp_session)?;
1303 Ok(())
1304 }
1305
1306 #[test]
test_parse_sdp_zero_length_string_fails()1307 fn test_parse_sdp_zero_length_string_fails() {
1308 assert!(parse_sdp("", true).is_err());
1309 }
1310
1311 #[test]
test_parse_sdp_to_short_string()1312 fn test_parse_sdp_to_short_string() {
1313 assert!(parse_sdp("fooooobarrrr", true).is_err());
1314 }
1315
1316 #[test]
test_parse_sdp_minimal_sdp_successfully() -> Result<(), SdpParserError>1317 fn test_parse_sdp_minimal_sdp_successfully() -> Result<(), SdpParserError> {
1318 parse_sdp(
1319 "v=0\r\n
1320 o=- 0 0 IN IP6 ::1\r\n
1321 s=-\r\n
1322 c=IN IP6 ::1\r\n
1323 t=0 0\r\n",
1324 true,
1325 )?;
1326 Ok(())
1327 }
1328
1329 #[test]
test_parse_sdp_too_short()1330 fn test_parse_sdp_too_short() {
1331 assert!(parse_sdp(
1332 "v=0\r\n
1333 o=- 0 0 IN IP4 0.0.0.0\r\n
1334 s=-\r\n",
1335 true
1336 )
1337 .is_err());
1338 }
1339
1340 #[test]
test_parse_sdp_line_error()1341 fn test_parse_sdp_line_error() {
1342 assert!(parse_sdp(
1343 "v=0\r\n
1344 o=- 0 0 IN IP4 0.0.0.0\r\n
1345 s=-\r\n
1346 t=0 foobar\r\n
1347 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n",
1348 true
1349 )
1350 .is_err());
1351 }
1352
1353 #[test]
test_parse_sdp_unsupported_error()1354 fn test_parse_sdp_unsupported_error() {
1355 assert!(parse_sdp(
1356 "v=0\r\n
1357 o=- 0 0 IN IP4 0.0.0.0\r\n
1358 s=-\r\n
1359 t=0 0\r\n
1360 m=foobar 0 UDP/TLS/RTP/SAVPF 0\r\n",
1361 true
1362 )
1363 .is_err());
1364 }
1365
1366 #[test]
test_parse_sdp_unsupported_warning() -> Result<(), SdpParserError>1367 fn test_parse_sdp_unsupported_warning() -> Result<(), SdpParserError> {
1368 parse_sdp(
1369 "v=0\r\n
1370 o=- 0 0 IN IP4 0.0.0.0\r\n
1371 s=-\r\n
1372 c=IN IP4 198.51.100.7\r\n
1373 t=0 0\r\n
1374 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n
1375 a=unsupported\r\n",
1376 false,
1377 )?;
1378 Ok(())
1379 }
1380
1381 #[test]
test_parse_sdp_sequence_error()1382 fn test_parse_sdp_sequence_error() {
1383 assert!(parse_sdp(
1384 "v=0\r\n
1385 o=- 0 0 IN IP4 0.0.0.0\r\n
1386 s=-\r\n
1387 t=0 0\r\n
1388 a=bundle-only\r\n
1389 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n",
1390 true
1391 )
1392 .is_err());
1393 }
1394
1395 #[test]
test_parse_sdp_integer_error()1396 fn test_parse_sdp_integer_error() {
1397 assert!(parse_sdp(
1398 "v=0\r\n
1399 o=- 0 0 IN IP4 0.0.0.0\r\n
1400 s=-\r\n
1401 t=0 0\r\n
1402 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n
1403 a=rtcp:34er21\r\n",
1404 true
1405 )
1406 .is_err());
1407 }
1408
1409 #[test]
test_parse_sdp_ipaddr_error()1410 fn test_parse_sdp_ipaddr_error() {
1411 assert!(parse_sdp(
1412 "v=0\r\n
1413 o=- 0 0 IN IP4 0.a.b.0\r\n
1414 s=-\r\n
1415 t=0 0\r\n
1416 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n",
1417 true
1418 )
1419 .is_err());
1420 }
1421
1422 #[test]
test_parse_sdp_invalid_session_attribute()1423 fn test_parse_sdp_invalid_session_attribute() {
1424 assert!(parse_sdp(
1425 "v=0\r\n
1426 o=- 0 0 IN IP4 0.a.b.0\r\n
1427 s=-\r\n
1428 t=0 0\r\n
1429 a=bundle-only\r\n
1430 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n",
1431 true
1432 )
1433 .is_err());
1434 }
1435
1436 #[test]
test_parse_sdp_invalid_media_attribute()1437 fn test_parse_sdp_invalid_media_attribute() {
1438 assert!(parse_sdp(
1439 "v=0\r\n
1440 o=- 0 0 IN IP4 0.a.b.0\r\n
1441 s=-\r\n
1442 t=0 0\r\n
1443 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n
1444 a=ice-lite\r\n",
1445 true
1446 )
1447 .is_err());
1448 }
1449
1450 #[test]
test_mask_origin()1451 fn test_mask_origin() {
1452 let mut anon = StatefulSdpAnonymizer::new();
1453 if let SdpType::Origin(origin_1) =
1454 parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0").unwrap()
1455 {
1456 for _ in 0..2 {
1457 let masked = origin_1.masked_clone(&mut anon);
1458 assert_eq!(masked.username, "origin-user-00000001");
1459 assert_eq!(
1460 masked.unicast_addr,
1461 ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(1)))
1462 );
1463 }
1464 } else {
1465 unreachable!();
1466 }
1467 }
1468
1469 #[test]
test_mask_sdp()1470 fn test_mask_sdp() {
1471 let mut anon = StatefulSdpAnonymizer::new();
1472 let sdp = parse_sdp(
1473 "v=0\r\n
1474 o=ausername 4294967296 2 IN IP4 127.0.0.1\r\n
1475 s=SIP Call\r\n
1476 c=IN IP4 198.51.100.7/51\r\n
1477 a=ice-pwd:12340\r\n
1478 a=ice-ufrag:4a799b2e\r\n
1479 a=fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC\r\n
1480 t=0 0\r\n
1481 m=video 56436 RTP/SAVPF 120\r\n
1482 a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host\r\n
1483 a=remote-candidates:0 10.0.0.1 5555\r\n
1484 a=rtpmap:120 VP8/90000\r\n",
1485 true,
1486 )
1487 .unwrap();
1488 let mut masked = sdp.masked_clone(&mut anon);
1489 assert_eq!(masked.origin.username, "origin-user-00000001");
1490 assert_eq!(
1491 masked.origin.unicast_addr,
1492 ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(1)))
1493 );
1494 assert_eq!(
1495 masked.connection.unwrap().address,
1496 ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(2)))
1497 );
1498 let mut attributes = masked.attribute;
1499 for m in &mut masked.media {
1500 for attribute in m.get_attributes() {
1501 attributes.push(attribute.clone());
1502 }
1503 }
1504 for attribute in attributes {
1505 match attribute {
1506 SdpAttribute::Candidate(c) => {
1507 assert_eq!(c.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(3))));
1508 assert_eq!(c.port, 1);
1509 }
1510 SdpAttribute::Fingerprint(f) => {
1511 assert_eq!(f.fingerprint, 1u64.to_byte_vec());
1512 }
1513 SdpAttribute::IcePwd(p) => {
1514 assert_eq!(p, "ice-password-00000001");
1515 }
1516 SdpAttribute::IceUfrag(u) => {
1517 assert_eq!(u, "ice-user-00000001");
1518 }
1519 SdpAttribute::RemoteCandidate(r) => {
1520 assert_eq!(r.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(4))));
1521 assert_eq!(r.port, 2);
1522 }
1523 _ => {}
1524 }
1525 }
1526 }
1527
1528 #[test]
test_parse_session_vector() -> Result<(), SdpParserError>1529 fn test_parse_session_vector() -> Result<(), SdpParserError> {
1530 let mut sdp_session = create_dummy_sdp_session();
1531 let mut lines: Vec<SdpLine> = Vec::new();
1532 lines.push(parse_sdp_line("a=sendrecv", 1)?);
1533 sdp_session.parse_session_vector(&mut lines)?;
1534 assert_eq!(sdp_session.attribute.len(), 1);
1535 Ok(())
1536 }
1537
1538 #[test]
test_parse_session_vector_non_session_attribute() -> Result<(), SdpParserError>1539 fn test_parse_session_vector_non_session_attribute() -> Result<(), SdpParserError> {
1540 let mut sdp_session = create_dummy_sdp_session();
1541 let mut lines: Vec<SdpLine> = Vec::new();
1542 lines.push(parse_sdp_line("a=bundle-only", 2)?);
1543 assert!(sdp_session.parse_session_vector(&mut lines).is_err());
1544 assert_eq!(sdp_session.attribute.len(), 0);
1545 Ok(())
1546 }
1547
1548 #[test]
test_parse_session_vector_version_repeated() -> Result<(), SdpParserError>1549 fn test_parse_session_vector_version_repeated() -> Result<(), SdpParserError> {
1550 let mut sdp_session = create_dummy_sdp_session();
1551 let mut lines: Vec<SdpLine> = Vec::new();
1552 lines.push(parse_sdp_line("v=0", 3)?);
1553 assert!(sdp_session.parse_session_vector(&mut lines).is_err());
1554 Ok(())
1555 }
1556
1557 #[test]
test_parse_session_vector_contains_media_type() -> Result<(), SdpParserError>1558 fn test_parse_session_vector_contains_media_type() -> Result<(), SdpParserError> {
1559 let mut sdp_session = create_dummy_sdp_session();
1560 let mut lines: Vec<SdpLine> = Vec::new();
1561 lines.push(parse_sdp_line("m=audio 0 UDP/TLS/RTP/SAVPF 0", 4)?);
1562 assert!(sdp_session.parse_session_vector(&mut lines).is_err());
1563 Ok(())
1564 }
1565
1566 #[test]
test_parse_sdp_vector_no_media_section() -> Result<(), SdpParserError>1567 fn test_parse_sdp_vector_no_media_section() -> Result<(), SdpParserError> {
1568 let mut lines: Vec<SdpLine> = Vec::new();
1569 lines.push(parse_sdp_line("v=0", 1)?);
1570 lines.push(parse_sdp_line(
1571 "o=ausername 4294967296 2 IN IP4 127.0.0.1",
1572 1,
1573 )?);
1574 lines.push(parse_sdp_line("s=SIP Call", 1)?);
1575 lines.push(parse_sdp_line("t=0 0", 1)?);
1576 lines.push(parse_sdp_line("c=IN IP6 ::1", 1)?);
1577 assert!(parse_sdp_vector(&mut lines).is_ok());
1578 Ok(())
1579 }
1580
1581 #[test]
test_parse_sdp_vector_with_media_section() -> Result<(), SdpParserError>1582 fn test_parse_sdp_vector_with_media_section() -> Result<(), SdpParserError> {
1583 let mut lines: Vec<SdpLine> = Vec::new();
1584 lines.push(parse_sdp_line("v=0", 1)?);
1585 lines.push(parse_sdp_line(
1586 "o=ausername 4294967296 2 IN IP4 127.0.0.1",
1587 1,
1588 )?);
1589 lines.push(parse_sdp_line("s=SIP Call", 1)?);
1590 lines.push(parse_sdp_line("t=0 0", 1)?);
1591 lines.push(parse_sdp_line("m=video 56436 RTP/SAVPF 120", 1)?);
1592 lines.push(parse_sdp_line("c=IN IP6 ::1", 1)?);
1593 assert!(parse_sdp_vector(&mut lines).is_ok());
1594 Ok(())
1595 }
1596
1597 #[test]
test_parse_sdp_vector_too_short() -> Result<(), SdpParserError>1598 fn test_parse_sdp_vector_too_short() -> Result<(), SdpParserError> {
1599 let mut lines: Vec<SdpLine> = Vec::new();
1600 lines.push(parse_sdp_line("v=0", 1)?);
1601 assert!(parse_sdp_vector(&mut lines).is_err());
1602 Ok(())
1603 }
1604
1605 #[test]
test_parse_sdp_vector_missing_version() -> Result<(), SdpParserError>1606 fn test_parse_sdp_vector_missing_version() -> Result<(), SdpParserError> {
1607 let mut lines: Vec<SdpLine> = Vec::new();
1608 lines.push(parse_sdp_line(
1609 "o=ausername 4294967296 2 IN IP4 127.0.0.1",
1610 1,
1611 )?);
1612 for _ in 0..3 {
1613 lines.push(parse_sdp_line("a=sendrecv", 1)?);
1614 }
1615 assert!(parse_sdp_vector(&mut lines).is_err());
1616 Ok(())
1617 }
1618
1619 #[test]
test_parse_sdp_vector_missing_origin() -> Result<(), SdpParserError>1620 fn test_parse_sdp_vector_missing_origin() -> Result<(), SdpParserError> {
1621 let mut lines: Vec<SdpLine> = Vec::new();
1622 lines.push(parse_sdp_line("v=0", 1)?);
1623 for _ in 0..3 {
1624 lines.push(parse_sdp_line("a=sendrecv", 1)?);
1625 }
1626 assert!(parse_sdp_vector(&mut lines).is_err());
1627 Ok(())
1628 }
1629
1630 #[test]
test_parse_sdp_vector_missing_session() -> Result<(), SdpParserError>1631 fn test_parse_sdp_vector_missing_session() -> Result<(), SdpParserError> {
1632 let mut lines: Vec<SdpLine> = Vec::new();
1633 lines.push(parse_sdp_line("v=0", 1)?);
1634 lines.push(parse_sdp_line(
1635 "o=ausername 4294967296 2 IN IP4 127.0.0.1",
1636 1,
1637 )?);
1638 for _ in 0..2 {
1639 lines.push(parse_sdp_line("a=sendrecv", 1)?);
1640 }
1641 assert!(parse_sdp_vector(&mut lines).is_err());
1642 Ok(())
1643 }
1644
1645 #[test]
test_session_add_media_works() -> Result<(), SdpParserError>1646 fn test_session_add_media_works() -> Result<(), SdpParserError> {
1647 let mut sdp_session = create_dummy_sdp_session();
1648 assert!(sdp_session
1649 .add_media(
1650 SdpMediaValue::Audio,
1651 SdpAttribute::Sendrecv,
1652 99,
1653 SdpProtocolValue::RtpSavpf,
1654 ExplicitlyTypedAddress::from(Ipv4Addr::new(127, 0, 0, 1))
1655 )
1656 .is_ok());
1657 assert!(sdp_session.get_connection().is_some());
1658 assert_eq!(sdp_session.attribute.len(), 0);
1659 assert_eq!(sdp_session.media.len(), 1);
1660 assert_eq!(sdp_session.media[0].get_attributes().len(), 1);
1661 assert!(sdp_session.media[0]
1662 .get_attribute(SdpAttributeType::Sendrecv)
1663 .is_some());
1664 Ok(())
1665 }
1666
1667 #[test]
test_session_add_media_invalid_attribute_fails() -> Result<(), SdpParserInternalError>1668 fn test_session_add_media_invalid_attribute_fails() -> Result<(), SdpParserInternalError> {
1669 let mut sdp_session = create_dummy_sdp_session();
1670 assert!(sdp_session
1671 .add_media(
1672 SdpMediaValue::Audio,
1673 SdpAttribute::IceLite,
1674 99,
1675 SdpProtocolValue::RtpSavpf,
1676 ExplicitlyTypedAddress::try_from((AddressType::IpV4, "127.0.0.1"))?
1677 )
1678 .is_err());
1679 Ok(())
1680 }
1681 }
1682