webrtc_ice/url/
mod.rs

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