xref: /webrtc/ice/src/url/mod.rs (revision 5d8fe953)
1 #[cfg(test)]
2 mod url_test;
3 
4 use crate::error::*;
5 
6 use std::borrow::Cow;
7 use std::convert::From;
8 use std::fmt;
9 
10 /// The type of server used in the ice.URL structure.
11 #[derive(PartialEq, Eq, Debug, Copy, Clone)]
12 pub enum SchemeType {
13     /// The URL represents a STUN server.
14     Stun,
15 
16     /// The URL represents a STUNS (secure) server.
17     Stuns,
18 
19     /// The URL represents a TURN server.
20     Turn,
21 
22     /// The URL represents a TURNS (secure) server.
23     Turns,
24 
25     /// Default public constant to use for "enum" like struct comparisons when no value was defined.
26     Unknown,
27 }
28 
29 impl Default for SchemeType {
default() -> Self30     fn default() -> Self {
31         Self::Unknown
32     }
33 }
34 
35 impl From<&str> for SchemeType {
36     /// Defines a procedure for creating a new `SchemeType` from a raw
37     /// string naming the scheme type.
from(raw: &str) -> Self38     fn from(raw: &str) -> Self {
39         match raw {
40             "stun" => Self::Stun,
41             "stuns" => Self::Stuns,
42             "turn" => Self::Turn,
43             "turns" => Self::Turns,
44             _ => Self::Unknown,
45         }
46     }
47 }
48 
49 impl fmt::Display for SchemeType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result50     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51         let s = match *self {
52             SchemeType::Stun => "stun",
53             SchemeType::Stuns => "stuns",
54             SchemeType::Turn => "turn",
55             SchemeType::Turns => "turns",
56             SchemeType::Unknown => "unknown",
57         };
58         write!(f, "{s}")
59     }
60 }
61 
62 /// The transport protocol type that is used in the `ice::url::Url` structure.
63 #[derive(PartialEq, Eq, Debug, Copy, Clone)]
64 pub enum ProtoType {
65     /// The URL uses a UDP transport.
66     Udp,
67 
68     /// The URL uses a TCP transport.
69     Tcp,
70 
71     Unknown,
72 }
73 
74 impl Default for ProtoType {
default() -> Self75     fn default() -> Self {
76         Self::Udp
77     }
78 }
79 
80 // defines a procedure for creating a new ProtoType from a raw
81 // string naming the transport protocol type.
82 impl From<&str> for ProtoType {
83     // NewSchemeType defines a procedure for creating a new SchemeType from a raw
84     // string naming the scheme type.
from(raw: &str) -> Self85     fn from(raw: &str) -> Self {
86         match raw {
87             "udp" => Self::Udp,
88             "tcp" => Self::Tcp,
89             _ => Self::Unknown,
90         }
91     }
92 }
93 
94 impl fmt::Display for ProtoType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result95     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96         let s = match *self {
97             Self::Udp => "udp",
98             Self::Tcp => "tcp",
99             Self::Unknown => "unknown",
100         };
101         write!(f, "{s}")
102     }
103 }
104 
105 /// Represents a STUN (rfc7064) or TURN (rfc7065) URL.
106 #[derive(Debug, Clone, Default)]
107 pub struct Url {
108     pub scheme: SchemeType,
109     pub host: String,
110     pub port: u16,
111     pub username: String,
112     pub password: String,
113     pub proto: ProtoType,
114 }
115 
116 impl fmt::Display for Url {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         let host = if self.host.contains("::") {
119             "[".to_owned() + self.host.as_str() + "]"
120         } else {
121             self.host.clone()
122         };
123         if self.scheme == SchemeType::Turn || self.scheme == SchemeType::Turns {
124             write!(
125                 f,
126                 "{}:{}:{}?transport={}",
127                 self.scheme, host, self.port, self.proto
128             )
129         } else {
130             write!(f, "{}:{}:{}", self.scheme, host, self.port)
131         }
132     }
133 }
134 
135 impl Url {
136     /// Parses a STUN or TURN urls following the ABNF syntax described in
137     /// [IETF rfc-7064](https://tools.ietf.org/html/rfc7064) and
138     /// [IETF rfc-7065](https://tools.ietf.org/html/rfc7065) respectively.
parse_url(raw: &str) -> Result<Self>139     pub fn parse_url(raw: &str) -> Result<Self> {
140         // work around for url crate
141         if raw.contains("//") {
142             return Err(Error::ErrInvalidUrl);
143         }
144 
145         let mut s = raw.to_string();
146         let pos = raw.find(':');
147         if let Some(p) = pos {
148             s.replace_range(p..=p, "://");
149         } else {
150             return Err(Error::ErrSchemeType);
151         }
152 
153         let raw_parts = url::Url::parse(&s)?;
154 
155         let scheme = raw_parts.scheme().into();
156 
157         let host = if let Some(host) = raw_parts.host_str() {
158             host.trim()
159                 .trim_start_matches('[')
160                 .trim_end_matches(']')
161                 .to_owned()
162         } else {
163             return Err(Error::ErrHost);
164         };
165 
166         let port = if let Some(port) = raw_parts.port() {
167             port
168         } else if scheme == SchemeType::Stun || scheme == SchemeType::Turn {
169             3478
170         } else {
171             5349
172         };
173 
174         let mut q_args = raw_parts.query_pairs();
175         let proto = match scheme {
176             SchemeType::Stun => {
177                 if q_args.count() > 0 {
178                     return Err(Error::ErrStunQuery);
179                 }
180                 ProtoType::Udp
181             }
182             SchemeType::Stuns => {
183                 if q_args.count() > 0 {
184                     return Err(Error::ErrStunQuery);
185                 }
186                 ProtoType::Tcp
187             }
188             SchemeType::Turn => {
189                 if q_args.count() > 1 {
190                     return Err(Error::ErrInvalidQuery);
191                 }
192                 if let Some((key, value)) = q_args.next() {
193                     if key == Cow::Borrowed("transport") {
194                         let proto: ProtoType = value.as_ref().into();
195                         if proto == ProtoType::Unknown {
196                             return Err(Error::ErrProtoType);
197                         }
198                         proto
199                     } else {
200                         return Err(Error::ErrInvalidQuery);
201                     }
202                 } else {
203                     ProtoType::Udp
204                 }
205             }
206             SchemeType::Turns => {
207                 if q_args.count() > 1 {
208                     return Err(Error::ErrInvalidQuery);
209                 }
210                 if let Some((key, value)) = q_args.next() {
211                     if key == Cow::Borrowed("transport") {
212                         let proto: ProtoType = value.as_ref().into();
213                         if proto == ProtoType::Unknown {
214                             return Err(Error::ErrProtoType);
215                         }
216                         proto
217                     } else {
218                         return Err(Error::ErrInvalidQuery);
219                     }
220                 } else {
221                     ProtoType::Tcp
222                 }
223             }
224             SchemeType::Unknown => {
225                 return Err(Error::ErrSchemeType);
226             }
227         };
228 
229         Ok(Self {
230             scheme,
231             host,
232             port,
233             username: "".to_owned(),
234             password: "".to_owned(),
235             proto,
236         })
237     }
238 
239     /*
240     fn parse_proto(raw:&str) ->Result<ProtoType> {
241         let qArgs= raw.split('=');
242         if qArgs.len() != 2 {
243             return Err(Error::ErrInvalidQuery.into());
244         }
245 
246         var proto ProtoType
247         if rawProto := qArgs.Get("transport"); rawProto != "" {
248             if proto = NewProtoType(rawProto); proto == ProtoType(0) {
249                 return ProtoType(Unknown), ErrProtoType
250             }
251             return proto, nil
252         }
253 
254         if len(qArgs) > 0 {
255             return ProtoType(Unknown), ErrInvalidQuery
256         }
257 
258         return proto, nil
259     }*/
260 
261     /// Returns whether the this URL's scheme describes secure scheme or not.
262     #[must_use]
is_secure(&self) -> bool263     pub fn is_secure(&self) -> bool {
264         self.scheme == SchemeType::Stuns || self.scheme == SchemeType::Turns
265     }
266 }
267