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