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