webrtc_ice/url/
mod.rs

1#[cfg(test)]
2mod url_test;
3
4use std::borrow::Cow;
5use std::convert::From;
6use std::fmt;
7
8use crate::error::*;
9
10/// The type of server used in the ice.URL structure.
11#[derive(PartialEq, Eq, Debug, Copy, Clone)]
12pub 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
29impl Default for SchemeType {
30    fn default() -> Self {
31        Self::Unknown
32    }
33}
34
35impl From<&str> for SchemeType {
36    /// Defines a procedure for creating a new `SchemeType` from a raw
37    /// string naming the scheme type.
38    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
49impl fmt::Display for SchemeType {
50    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)]
64pub 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
74impl Default for ProtoType {
75    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.
82impl From<&str> for ProtoType {
83    // NewSchemeType defines a procedure for creating a new SchemeType from a raw
84    // string naming the scheme type.
85    fn from(raw: &str) -> Self {
86        match raw {
87            "udp" => Self::Udp,
88            "tcp" => Self::Tcp,
89            _ => Self::Unknown,
90        }
91    }
92}
93
94impl fmt::Display for ProtoType {
95    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)]
107pub 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
116impl fmt::Display for Url {
117    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
135impl 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.
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]
263    pub fn is_secure(&self) -> bool {
264        self.scheme == SchemeType::Stuns || self.scheme == SchemeType::Turns
265    }
266}