Skip to main content

rtc_ice/url/
mod.rs

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