1 use sdp::description::session::SessionDescription;
2 use sdp::util::ConnectionRole;
3 
4 use serde::{Deserialize, Serialize};
5 use std::fmt;
6 
7 /// DtlsRole indicates the role of the DTLS transport.
8 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
9 pub enum DTLSRole {
10     #[default]
11     Unspecified = 0,
12 
13     /// DTLSRoleAuto defines the DTLS role is determined based on
14     /// the resolved ICE role: the ICE controlled role acts as the DTLS
15     /// client and the ICE controlling role acts as the DTLS server.
16     #[serde(rename = "auto")]
17     Auto = 1,
18 
19     /// DTLSRoleClient defines the DTLS client role.
20     #[serde(rename = "client")]
21     Client = 2,
22 
23     /// DTLSRoleServer defines the DTLS server role.
24     #[serde(rename = "server")]
25     Server = 3,
26 }
27 
28 /// <https://tools.ietf.org/html/rfc5763>
29 /// The answerer MUST use either a
30 /// setup attribute value of setup:active or setup:passive.  Note that
31 /// if the answerer uses setup:passive, then the DTLS handshake will
32 /// not begin until the answerer is received, which adds additional
33 /// latency. setup:active allows the answer and the DTLS handshake to
34 /// occur in parallel.  Thus, setup:active is RECOMMENDED.
35 pub(crate) const DEFAULT_DTLS_ROLE_ANSWER: DTLSRole = DTLSRole::Client;
36 
37 /// The endpoint that is the offerer MUST use the setup attribute
38 /// value of setup:actpass and be prepared to receive a client_hello
39 /// before it receives the answer.
40 pub(crate) const DEFAULT_DTLS_ROLE_OFFER: DTLSRole = DTLSRole::Auto;
41 
42 impl fmt::Display for DTLSRole {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result43     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44         match *self {
45             DTLSRole::Auto => write!(f, "auto"),
46             DTLSRole::Client => write!(f, "client"),
47             DTLSRole::Server => write!(f, "server"),
48             _ => write!(f, "{}", crate::UNSPECIFIED_STR),
49         }
50     }
51 }
52 
53 /// Iterate a SessionDescription from a remote to determine if an explicit
54 /// role can been determined from it. The decision is made from the first role we we parse.
55 /// If no role can be found we return DTLSRoleAuto
56 impl From<&SessionDescription> for DTLSRole {
from(session_description: &SessionDescription) -> Self57     fn from(session_description: &SessionDescription) -> Self {
58         for media_section in &session_description.media_descriptions {
59             for attribute in &media_section.attributes {
60                 if attribute.key == "setup" {
61                     if let Some(value) = &attribute.value {
62                         match value.as_str() {
63                             "active" => return DTLSRole::Client,
64                             "passive" => return DTLSRole::Server,
65                             _ => return DTLSRole::Auto,
66                         };
67                     } else {
68                         return DTLSRole::Auto;
69                     }
70                 }
71             }
72         }
73 
74         DTLSRole::Auto
75     }
76 }
77 
78 impl DTLSRole {
to_connection_role(self) -> ConnectionRole79     pub(crate) fn to_connection_role(self) -> ConnectionRole {
80         match self {
81             DTLSRole::Client => ConnectionRole::Active,
82             DTLSRole::Server => ConnectionRole::Passive,
83             DTLSRole::Auto => ConnectionRole::Actpass,
84             _ => ConnectionRole::Unspecified,
85         }
86     }
87 }
88 
89 #[cfg(test)]
90 mod test {
91     use crate::error::Result;
92 
93     use super::*;
94 
95     use std::io::Cursor;
96 
97     #[test]
test_dtls_role_string()98     fn test_dtls_role_string() {
99         let tests = vec![
100             (DTLSRole::Unspecified, "Unspecified"),
101             (DTLSRole::Auto, "auto"),
102             (DTLSRole::Client, "client"),
103             (DTLSRole::Server, "server"),
104         ];
105 
106         for (role, expected_string) in tests {
107             assert_eq!(role.to_string(), expected_string)
108         }
109     }
110 
111     #[test]
test_dtls_role_from_remote_sdp() -> Result<()>112     fn test_dtls_role_from_remote_sdp() -> Result<()> {
113         const NO_MEDIA: &str = "v=0
114 o=- 4596489990601351948 2 IN IP4 127.0.0.1
115 s=-
116 t=0 0
117 ";
118 
119         const MEDIA_NO_SETUP: &str = "v=0
120 o=- 4596489990601351948 2 IN IP4 127.0.0.1
121 s=-
122 t=0 0
123 m=application 47299 DTLS/SCTP 5000
124 c=IN IP4 192.168.20.129
125 ";
126 
127         const MEDIA_SETUP_DECLARED: &str = "v=0
128 o=- 4596489990601351948 2 IN IP4 127.0.0.1
129 s=-
130 t=0 0
131 m=application 47299 DTLS/SCTP 5000
132 c=IN IP4 192.168.20.129
133 a=setup:";
134 
135         let tests = vec![
136             ("No MediaDescriptions", NO_MEDIA.to_owned(), DTLSRole::Auto),
137             (
138                 "MediaDescription, no setup",
139                 MEDIA_NO_SETUP.to_owned(),
140                 DTLSRole::Auto,
141             ),
142             (
143                 "MediaDescription, setup:actpass",
144                 format!("{}{}\n", MEDIA_SETUP_DECLARED, "actpass"),
145                 DTLSRole::Auto,
146             ),
147             (
148                 "MediaDescription, setup:passive",
149                 format!("{}{}\n", MEDIA_SETUP_DECLARED, "passive"),
150                 DTLSRole::Server,
151             ),
152             (
153                 "MediaDescription, setup:active",
154                 format!("{}{}\n", MEDIA_SETUP_DECLARED, "active"),
155                 DTLSRole::Client,
156             ),
157         ];
158 
159         for (name, session_description_str, expected_role) in tests {
160             let mut reader = Cursor::new(session_description_str.as_bytes());
161             let session_description = SessionDescription::unmarshal(&mut reader)?;
162             assert_eq!(
163                 DTLSRole::from(&session_description),
164                 expected_role,
165                 "{name} failed"
166             );
167         }
168 
169         Ok(())
170     }
171 }
172