1 | //! DNS name validation according to RFC1035, but with underscores allowed. |
2 | |
3 | #[cfg (all(feature = "alloc" , feature = "std" ))] |
4 | use alloc::borrow::Cow; |
5 | #[cfg (feature = "alloc" )] |
6 | use alloc::string::{String, ToString}; |
7 | use core::hash::{Hash, Hasher}; |
8 | use core::{fmt, mem, str}; |
9 | #[cfg (feature = "std" )] |
10 | use std::error::Error as StdError; |
11 | |
12 | /// Encodes ways a client can know the expected name of the server. |
13 | /// |
14 | /// This currently covers knowing the DNS name of the server, but |
15 | /// will be extended in the future to supporting privacy-preserving names |
16 | /// for the server ("ECH"). For this reason this enum is `non_exhaustive`. |
17 | /// |
18 | /// # Making one |
19 | /// |
20 | /// If you have a DNS name as a `&str`, this type implements `TryFrom<&str>`, |
21 | /// so you can do: |
22 | /// |
23 | /// ``` |
24 | /// # use rustls_pki_types::ServerName; |
25 | /// ServerName::try_from("example.com" ).expect("invalid DNS name" ); |
26 | /// ``` |
27 | /// |
28 | /// If you have an owned `String`, you can use `TryFrom` directly: |
29 | /// |
30 | /// ``` |
31 | /// # use rustls_pki_types::ServerName; |
32 | /// let name = "example.com" .to_string(); |
33 | /// #[cfg(feature = "alloc" )] |
34 | /// ServerName::try_from(name).expect("invalid DNS name" ); |
35 | /// ``` |
36 | /// |
37 | /// which will yield a `ServerName<'static>` if successful. |
38 | /// |
39 | /// or, alternatively... |
40 | /// |
41 | /// ``` |
42 | /// # use rustls_pki_types::ServerName; |
43 | /// let x: ServerName = "example.com" .try_into().expect("invalid DNS name" ); |
44 | /// ``` |
45 | #[non_exhaustive ] |
46 | #[derive (Clone, Eq, Hash, PartialEq)] |
47 | pub enum ServerName<'a> { |
48 | /// The server is identified by a DNS name. The name |
49 | /// is sent in the TLS Server Name Indication (SNI) |
50 | /// extension. |
51 | DnsName(DnsName<'a>), |
52 | |
53 | /// The server is identified by an IP address. SNI is not |
54 | /// done. |
55 | IpAddress(IpAddr), |
56 | } |
57 | |
58 | impl ServerName<'_> { |
59 | /// Produce an owned `ServerName` from this (potentially borrowed) `ServerName`. |
60 | #[cfg (feature = "alloc" )] |
61 | pub fn to_owned(&self) -> ServerName<'static> { |
62 | match self { |
63 | Self::DnsName(d: &DnsName<'_>) => ServerName::DnsName(d.to_owned()), |
64 | Self::IpAddress(i: &IpAddr) => ServerName::IpAddress(*i), |
65 | } |
66 | } |
67 | |
68 | /// Return the string representation of this `ServerName`. |
69 | /// |
70 | /// In the case of a `ServerName::DnsName` instance, this function returns a borrowed `str`. |
71 | /// For a `ServerName::IpAddress` instance it returns an allocated `String`. |
72 | #[cfg (feature = "std" )] |
73 | pub fn to_str(&self) -> Cow<'_, str> { |
74 | match self { |
75 | Self::DnsName(d: &DnsName<'_>) => d.as_ref().into(), |
76 | Self::IpAddress(i: &IpAddr) => std::net::IpAddr::from(*i).to_string().into(), |
77 | } |
78 | } |
79 | } |
80 | |
81 | impl fmt::Debug for ServerName<'_> { |
82 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
83 | match self { |
84 | Self::DnsName(d: &DnsName<'_>) => f.debug_tuple(name:"DnsName" ).field(&d.as_ref()).finish(), |
85 | Self::IpAddress(i: &IpAddr) => f.debug_tuple(name:"IpAddress" ).field(i).finish(), |
86 | } |
87 | } |
88 | } |
89 | |
90 | #[cfg (feature = "alloc" )] |
91 | impl TryFrom<String> for ServerName<'static> { |
92 | type Error = InvalidDnsNameError; |
93 | |
94 | fn try_from(value: String) -> Result<Self, Self::Error> { |
95 | match DnsName::try_from_string(value) { |
96 | Ok(dns: DnsName<'_>) => Ok(Self::DnsName(dns)), |
97 | Err(value: String) => match IpAddr::try_from(value.as_str()) { |
98 | Ok(ip: IpAddr) => Ok(Self::IpAddress(ip)), |
99 | Err(_) => Err(InvalidDnsNameError), |
100 | }, |
101 | } |
102 | } |
103 | } |
104 | |
105 | impl<'a> TryFrom<&'a [u8]> for ServerName<'a> { |
106 | type Error = InvalidDnsNameError; |
107 | |
108 | fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { |
109 | match str::from_utf8(value) { |
110 | Ok(s: &str) => Self::try_from(s), |
111 | Err(_) => Err(InvalidDnsNameError), |
112 | } |
113 | } |
114 | } |
115 | |
116 | /// Attempt to make a ServerName from a string by parsing as a DNS name or IP address. |
117 | impl<'a> TryFrom<&'a str> for ServerName<'a> { |
118 | type Error = InvalidDnsNameError; |
119 | fn try_from(s: &'a str) -> Result<Self, Self::Error> { |
120 | match DnsName::try_from(s) { |
121 | Ok(dns: DnsName<'_>) => Ok(Self::DnsName(dns)), |
122 | Err(InvalidDnsNameError) => match IpAddr::try_from(s) { |
123 | Ok(ip: IpAddr) => Ok(Self::IpAddress(ip)), |
124 | Err(_) => Err(InvalidDnsNameError), |
125 | }, |
126 | } |
127 | } |
128 | } |
129 | |
130 | impl From<IpAddr> for ServerName<'_> { |
131 | fn from(addr: IpAddr) -> Self { |
132 | Self::IpAddress(addr) |
133 | } |
134 | } |
135 | |
136 | #[cfg (feature = "std" )] |
137 | impl From<std::net::IpAddr> for ServerName<'_> { |
138 | fn from(addr: std::net::IpAddr) -> Self { |
139 | Self::IpAddress(addr.into()) |
140 | } |
141 | } |
142 | |
143 | impl From<Ipv4Addr> for ServerName<'_> { |
144 | fn from(v4: Ipv4Addr) -> Self { |
145 | Self::IpAddress(IpAddr::V4(v4)) |
146 | } |
147 | } |
148 | |
149 | impl From<Ipv6Addr> for ServerName<'_> { |
150 | fn from(v6: Ipv6Addr) -> Self { |
151 | Self::IpAddress(IpAddr::V6(v6)) |
152 | } |
153 | } |
154 | |
155 | #[cfg (feature = "std" )] |
156 | impl From<std::net::Ipv4Addr> for ServerName<'_> { |
157 | fn from(v4: std::net::Ipv4Addr) -> Self { |
158 | Self::IpAddress(IpAddr::V4(v4.into())) |
159 | } |
160 | } |
161 | |
162 | #[cfg (feature = "std" )] |
163 | impl From<std::net::Ipv6Addr> for ServerName<'_> { |
164 | fn from(v6: std::net::Ipv6Addr) -> Self { |
165 | Self::IpAddress(IpAddr::V6(v6.into())) |
166 | } |
167 | } |
168 | |
169 | /// A type which encapsulates a string (borrowed or owned) that is a syntactically valid DNS name. |
170 | #[derive (Clone, Debug, Eq, Hash, PartialEq)] |
171 | pub struct DnsName<'a>(DnsNameInner<'a>); |
172 | |
173 | impl<'a> DnsName<'a> { |
174 | /// Produce a borrowed `DnsName` from this owned `DnsName`. |
175 | pub fn borrow(&'a self) -> Self { |
176 | Self(match self { |
177 | Self(DnsNameInner::Borrowed(s)) => DnsNameInner::Borrowed(s), |
178 | #[cfg (feature = "alloc" )] |
179 | Self(DnsNameInner::Owned(s)) => DnsNameInner::Borrowed(s.as_str()), |
180 | }) |
181 | } |
182 | |
183 | /// Copy this object to produce an owned `DnsName`, smashing the case to lowercase |
184 | /// in one operation. |
185 | #[cfg (feature = "alloc" )] |
186 | pub fn to_lowercase_owned(&self) -> DnsName<'static> { |
187 | DnsName(DnsNameInner::Owned(self.as_ref().to_ascii_lowercase())) |
188 | } |
189 | |
190 | /// Produce an owned `DnsName` from this (potentially borrowed) `DnsName`. |
191 | #[cfg (feature = "alloc" )] |
192 | pub fn to_owned(&self) -> DnsName<'static> { |
193 | DnsName(DnsNameInner::Owned(match self { |
194 | Self(DnsNameInner::Borrowed(s)) => s.to_string(), |
195 | #[cfg (feature = "alloc" )] |
196 | Self(DnsNameInner::Owned(s)) => s.clone(), |
197 | })) |
198 | } |
199 | |
200 | #[cfg (feature = "alloc" )] |
201 | fn try_from_string(s: String) -> Result<Self, String> { |
202 | match validate(s.as_bytes()) { |
203 | Ok(_) => Ok(Self(DnsNameInner::Owned(s))), |
204 | Err(_) => Err(s), |
205 | } |
206 | } |
207 | |
208 | /// Produces a borrowed [`DnsName`] from a borrowed [`str`]. |
209 | pub const fn try_from_str(s: &str) -> Result<DnsName<'_>, InvalidDnsNameError> { |
210 | match validate(s.as_bytes()) { |
211 | Ok(_) => Ok(DnsName(DnsNameInner::Borrowed(s))), |
212 | Err(err) => Err(err), |
213 | } |
214 | } |
215 | } |
216 | |
217 | #[cfg (feature = "alloc" )] |
218 | impl TryFrom<String> for DnsName<'static> { |
219 | type Error = InvalidDnsNameError; |
220 | |
221 | fn try_from(value: String) -> Result<Self, Self::Error> { |
222 | Self::try_from_string(value).map_err(|_| InvalidDnsNameError) |
223 | } |
224 | } |
225 | |
226 | impl<'a> TryFrom<&'a str> for DnsName<'a> { |
227 | type Error = InvalidDnsNameError; |
228 | |
229 | fn try_from(value: &'a str) -> Result<Self, Self::Error> { |
230 | DnsName::try_from_str(value) |
231 | } |
232 | } |
233 | |
234 | impl<'a> TryFrom<&'a [u8]> for DnsName<'a> { |
235 | type Error = InvalidDnsNameError; |
236 | |
237 | fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { |
238 | validate(input:value)?; |
239 | Ok(Self(DnsNameInner::Borrowed(str::from_utf8(value).unwrap()))) |
240 | } |
241 | } |
242 | |
243 | impl AsRef<str> for DnsName<'_> { |
244 | fn as_ref(&self) -> &str { |
245 | match self { |
246 | Self(DnsNameInner::Borrowed(s: &&str)) => s, |
247 | #[cfg (feature = "alloc" )] |
248 | Self(DnsNameInner::Owned(s: &String)) => s.as_str(), |
249 | } |
250 | } |
251 | } |
252 | |
253 | #[derive (Clone, Eq)] |
254 | enum DnsNameInner<'a> { |
255 | Borrowed(&'a str), |
256 | #[cfg (feature = "alloc" )] |
257 | Owned(String), |
258 | } |
259 | |
260 | impl PartialEq<Self> for DnsNameInner<'_> { |
261 | fn eq(&self, other: &Self) -> bool { |
262 | match (self, other) { |
263 | (Self::Borrowed(s: &&str), Self::Borrowed(o: &&str)) => s.eq_ignore_ascii_case(o), |
264 | #[cfg (feature = "alloc" )] |
265 | (Self::Borrowed(s: &&str), Self::Owned(o: &String)) => s.eq_ignore_ascii_case(o.as_str()), |
266 | #[cfg (feature = "alloc" )] |
267 | (Self::Owned(s: &String), Self::Borrowed(o: &&str)) => s.eq_ignore_ascii_case(o), |
268 | #[cfg (feature = "alloc" )] |
269 | (Self::Owned(s: &String), Self::Owned(o: &String)) => s.eq_ignore_ascii_case(o.as_str()), |
270 | } |
271 | } |
272 | } |
273 | |
274 | impl Hash for DnsNameInner<'_> { |
275 | fn hash<H: Hasher>(&self, state: &mut H) { |
276 | let s: &str = match self { |
277 | Self::Borrowed(s: &&str) => s, |
278 | #[cfg (feature = "alloc" )] |
279 | Self::Owned(s: &String) => s.as_str(), |
280 | }; |
281 | |
282 | s.chars().for_each(|c: char| c.to_ascii_lowercase().hash(state)); |
283 | } |
284 | } |
285 | |
286 | impl fmt::Debug for DnsNameInner<'_> { |
287 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
288 | match self { |
289 | Self::Borrowed(s: &&str) => f.write_fmt(format_args!(" {:?}" , s)), |
290 | #[cfg (feature = "alloc" )] |
291 | Self::Owned(s: &String) => f.write_fmt(format_args!(" {:?}" , s)), |
292 | } |
293 | } |
294 | } |
295 | |
296 | /// The provided input could not be parsed because |
297 | /// it is not a syntactically-valid DNS Name. |
298 | #[derive (Debug)] |
299 | pub struct InvalidDnsNameError; |
300 | |
301 | impl fmt::Display for InvalidDnsNameError { |
302 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
303 | f.write_str(data:"invalid dns name" ) |
304 | } |
305 | } |
306 | |
307 | #[cfg (feature = "std" )] |
308 | impl StdError for InvalidDnsNameError {} |
309 | |
310 | const fn validate(input: &[u8]) -> Result<(), InvalidDnsNameError> { |
311 | enum State { |
312 | Start, |
313 | Next, |
314 | NumericOnly { len: usize }, |
315 | NextAfterNumericOnly, |
316 | Subsequent { len: usize }, |
317 | Hyphen { len: usize }, |
318 | } |
319 | |
320 | use State::*; |
321 | let mut state = Start; |
322 | |
323 | /// "Labels must be 63 characters or less." |
324 | const MAX_LABEL_LENGTH: usize = 63; |
325 | |
326 | /// https://devblogs.microsoft.com/oldnewthing/20120412-00/?p=7873 |
327 | const MAX_NAME_LENGTH: usize = 253; |
328 | |
329 | if input.len() > MAX_NAME_LENGTH { |
330 | return Err(InvalidDnsNameError); |
331 | } |
332 | |
333 | let mut idx = 0; |
334 | while idx < input.len() { |
335 | let ch = input[idx]; |
336 | state = match (state, ch) { |
337 | (Start | Next | NextAfterNumericOnly | Hyphen { .. }, b'.' ) => { |
338 | return Err(InvalidDnsNameError) |
339 | } |
340 | (Subsequent { .. }, b'.' ) => Next, |
341 | (NumericOnly { .. }, b'.' ) => NextAfterNumericOnly, |
342 | (Subsequent { len } | NumericOnly { len } | Hyphen { len }, _) |
343 | if len >= MAX_LABEL_LENGTH => |
344 | { |
345 | return Err(InvalidDnsNameError) |
346 | } |
347 | (Start | Next | NextAfterNumericOnly, b'0' ..=b'9' ) => NumericOnly { len: 1 }, |
348 | (NumericOnly { len }, b'0' ..=b'9' ) => NumericOnly { len: len + 1 }, |
349 | (Start | Next | NextAfterNumericOnly, b'a' ..=b'z' | b'A' ..=b'Z' | b'_' ) => { |
350 | Subsequent { len: 1 } |
351 | } |
352 | (Subsequent { len } | NumericOnly { len } | Hyphen { len }, b'-' ) => { |
353 | Hyphen { len: len + 1 } |
354 | } |
355 | ( |
356 | Subsequent { len } | NumericOnly { len } | Hyphen { len }, |
357 | b'a' ..=b'z' | b'A' ..=b'Z' | b'_' | b'0' ..=b'9' , |
358 | ) => Subsequent { len: len + 1 }, |
359 | _ => return Err(InvalidDnsNameError), |
360 | }; |
361 | idx += 1; |
362 | } |
363 | |
364 | if matches!( |
365 | state, |
366 | Start | Hyphen { .. } | NumericOnly { .. } | NextAfterNumericOnly |
367 | ) { |
368 | return Err(InvalidDnsNameError); |
369 | } |
370 | |
371 | Ok(()) |
372 | } |
373 | |
374 | /// `no_std` implementation of `std::net::IpAddr`. |
375 | /// |
376 | /// Note: because we intend to replace this type with `core::net::IpAddr` as soon as it is |
377 | /// stabilized, the identity of this type should not be considered semver-stable. However, the |
378 | /// attached interfaces are stable; they form a subset of those provided by `core::net::IpAddr`. |
379 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
380 | pub enum IpAddr { |
381 | /// An Ipv4 address. |
382 | V4(Ipv4Addr), |
383 | /// An Ipv6 address. |
384 | V6(Ipv6Addr), |
385 | } |
386 | |
387 | impl TryFrom<&str> for IpAddr { |
388 | type Error = AddrParseError; |
389 | |
390 | fn try_from(value: &str) -> Result<Self, Self::Error> { |
391 | match Ipv4Addr::try_from(value) { |
392 | Ok(v4: Ipv4Addr) => Ok(Self::V4(v4)), |
393 | Err(_) => match Ipv6Addr::try_from(value) { |
394 | Ok(v6: Ipv6Addr) => Ok(Self::V6(v6)), |
395 | Err(e: AddrParseError) => Err(e), |
396 | }, |
397 | } |
398 | } |
399 | } |
400 | |
401 | #[cfg (feature = "std" )] |
402 | impl From<std::net::IpAddr> for IpAddr { |
403 | fn from(addr: std::net::IpAddr) -> Self { |
404 | match addr { |
405 | std::net::IpAddr::V4(v4: Ipv4Addr) => Self::V4(v4.into()), |
406 | std::net::IpAddr::V6(v6: Ipv6Addr) => Self::V6(v6.into()), |
407 | } |
408 | } |
409 | } |
410 | |
411 | #[cfg (feature = "std" )] |
412 | impl From<IpAddr> for std::net::IpAddr { |
413 | fn from(value: IpAddr) -> Self { |
414 | match value { |
415 | IpAddr::V4(v4: Ipv4Addr) => Self::from(std::net::Ipv4Addr::from(v4)), |
416 | IpAddr::V6(v6: Ipv6Addr) => Self::from(std::net::Ipv6Addr::from(v6)), |
417 | } |
418 | } |
419 | } |
420 | |
421 | #[cfg (feature = "std" )] |
422 | impl From<std::net::Ipv4Addr> for IpAddr { |
423 | fn from(v4: std::net::Ipv4Addr) -> Self { |
424 | Self::V4(v4.into()) |
425 | } |
426 | } |
427 | |
428 | #[cfg (feature = "std" )] |
429 | impl From<std::net::Ipv6Addr> for IpAddr { |
430 | fn from(v6: std::net::Ipv6Addr) -> Self { |
431 | Self::V6(v6.into()) |
432 | } |
433 | } |
434 | |
435 | /// `no_std` implementation of `std::net::Ipv4Addr`. |
436 | /// |
437 | /// Note: because we intend to replace this type with `core::net::Ipv4Addr` as soon as it is |
438 | /// stabilized, the identity of this type should not be considered semver-stable. However, the |
439 | /// attached interfaces are stable; they form a subset of those provided by `core::net::Ipv4Addr`. |
440 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
441 | pub struct Ipv4Addr([u8; 4]); |
442 | |
443 | impl TryFrom<&str> for Ipv4Addr { |
444 | type Error = AddrParseError; |
445 | |
446 | fn try_from(value: &str) -> Result<Self, Self::Error> { |
447 | // don't try to parse if too long |
448 | if value.len() > 15 { |
449 | Err(AddrParseError(AddrKind::Ipv4)) |
450 | } else { |
451 | Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv4_addr(), kind:AddrKind::Ipv4) |
452 | } |
453 | } |
454 | } |
455 | |
456 | #[cfg (feature = "std" )] |
457 | impl From<std::net::Ipv4Addr> for Ipv4Addr { |
458 | fn from(addr: std::net::Ipv4Addr) -> Self { |
459 | Self(addr.octets()) |
460 | } |
461 | } |
462 | |
463 | #[cfg (feature = "std" )] |
464 | impl From<Ipv4Addr> for std::net::Ipv4Addr { |
465 | fn from(value: Ipv4Addr) -> Self { |
466 | Self::from(value.0) |
467 | } |
468 | } |
469 | |
470 | impl AsRef<[u8; 4]> for Ipv4Addr { |
471 | fn as_ref(&self) -> &[u8; 4] { |
472 | &self.0 |
473 | } |
474 | } |
475 | |
476 | /// `no_std` implementation of `std::net::Ipv6Addr`. |
477 | /// |
478 | /// Note: because we intend to replace this type with `core::net::Ipv6Addr` as soon as it is |
479 | /// stabilized, the identity of this type should not be considered semver-stable. However, the |
480 | /// attached interfaces are stable; they form a subset of those provided by `core::net::Ipv6Addr`. |
481 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
482 | pub struct Ipv6Addr([u8; 16]); |
483 | |
484 | impl TryFrom<&str> for Ipv6Addr { |
485 | type Error = AddrParseError; |
486 | |
487 | fn try_from(value: &str) -> Result<Self, Self::Error> { |
488 | Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv6_addr(), kind:AddrKind::Ipv6) |
489 | } |
490 | } |
491 | |
492 | impl From<[u16; 8]> for Ipv6Addr { |
493 | fn from(value: [u16; 8]) -> Self { |
494 | // Adapted from `std::net::Ipv6Addr::new()` |
495 | let addr16: [u16; 8] = [ |
496 | value[0].to_be(), |
497 | value[1].to_be(), |
498 | value[2].to_be(), |
499 | value[3].to_be(), |
500 | value[4].to_be(), |
501 | value[5].to_be(), |
502 | value[6].to_be(), |
503 | value[7].to_be(), |
504 | ]; |
505 | Self( |
506 | // All elements in `addr16` are big endian. |
507 | // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. |
508 | unsafe { mem::transmute::<[u16; 8], [u8; 16]>(src:addr16) }, |
509 | ) |
510 | } |
511 | } |
512 | |
513 | #[cfg (feature = "std" )] |
514 | impl From<std::net::Ipv6Addr> for Ipv6Addr { |
515 | fn from(addr: std::net::Ipv6Addr) -> Self { |
516 | Self(addr.octets()) |
517 | } |
518 | } |
519 | |
520 | #[cfg (feature = "std" )] |
521 | impl From<Ipv6Addr> for std::net::Ipv6Addr { |
522 | fn from(value: Ipv6Addr) -> Self { |
523 | Self::from(value.0) |
524 | } |
525 | } |
526 | |
527 | impl AsRef<[u8; 16]> for Ipv6Addr { |
528 | fn as_ref(&self) -> &[u8; 16] { |
529 | &self.0 |
530 | } |
531 | } |
532 | |
533 | // Adapted from core, 2023-11-23 |
534 | // |
535 | // https://github.com/rust-lang/rust/blob/fc13ca6d70f7381513c22443fc5aaee1d151ea45/library/core/src/net/parser.rs#L34 |
536 | mod parser { |
537 | use super::{AddrParseError, Ipv4Addr, Ipv6Addr}; |
538 | |
539 | pub(super) struct Parser<'a> { |
540 | // Parsing as ASCII, so can use byte array. |
541 | state: &'a [u8], |
542 | } |
543 | |
544 | impl<'a> Parser<'a> { |
545 | pub(super) fn new(input: &'a [u8]) -> Self { |
546 | Parser { state: input } |
547 | } |
548 | |
549 | /// Run a parser, and restore the pre-parse state if it fails. |
550 | fn read_atomically<T, F>(&mut self, inner: F) -> Option<T> |
551 | where |
552 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
553 | { |
554 | let state = self.state; |
555 | let result = inner(self); |
556 | if result.is_none() { |
557 | self.state = state; |
558 | } |
559 | result |
560 | } |
561 | |
562 | /// Run a parser, but fail if the entire input wasn't consumed. |
563 | /// Doesn't run atomically. |
564 | pub(super) fn parse_with<T, F>( |
565 | &mut self, |
566 | inner: F, |
567 | kind: AddrKind, |
568 | ) -> Result<T, AddrParseError> |
569 | where |
570 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
571 | { |
572 | let result = inner(self); |
573 | if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind)) |
574 | } |
575 | |
576 | /// Peek the next character from the input |
577 | fn peek_char(&self) -> Option<char> { |
578 | self.state.first().map(|&b| char::from(b)) |
579 | } |
580 | |
581 | /// Read the next character from the input |
582 | fn read_char(&mut self) -> Option<char> { |
583 | self.state.split_first().map(|(&b, tail)| { |
584 | self.state = tail; |
585 | char::from(b) |
586 | }) |
587 | } |
588 | |
589 | #[must_use ] |
590 | /// Read the next character from the input if it matches the target. |
591 | fn read_given_char(&mut self, target: char) -> Option<()> { |
592 | self.read_atomically(|p| { |
593 | p.read_char() |
594 | .and_then(|c| if c == target { Some(()) } else { None }) |
595 | }) |
596 | } |
597 | |
598 | /// Helper for reading separators in an indexed loop. Reads the separator |
599 | /// character iff index > 0, then runs the parser. When used in a loop, |
600 | /// the separator character will only be read on index > 0 (see |
601 | /// read_ipv4_addr for an example) |
602 | fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T> |
603 | where |
604 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
605 | { |
606 | self.read_atomically(move |p| { |
607 | if index > 0 { |
608 | p.read_given_char(sep)?; |
609 | } |
610 | inner(p) |
611 | }) |
612 | } |
613 | |
614 | // Read a number off the front of the input in the given radix, stopping |
615 | // at the first non-digit character or eof. Fails if the number has more |
616 | // digits than max_digits or if there is no number. |
617 | fn read_number<T: ReadNumberHelper>( |
618 | &mut self, |
619 | radix: u32, |
620 | max_digits: Option<usize>, |
621 | allow_zero_prefix: bool, |
622 | ) -> Option<T> { |
623 | self.read_atomically(move |p| { |
624 | let mut result = T::ZERO; |
625 | let mut digit_count = 0; |
626 | let has_leading_zero = p.peek_char() == Some('0' ); |
627 | |
628 | while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
629 | result = result.checked_mul(radix)?; |
630 | result = result.checked_add(digit)?; |
631 | digit_count += 1; |
632 | if let Some(max_digits) = max_digits { |
633 | if digit_count > max_digits { |
634 | return None; |
635 | } |
636 | } |
637 | } |
638 | |
639 | if digit_count == 0 || (!allow_zero_prefix && has_leading_zero && digit_count > 1) { |
640 | None |
641 | } else { |
642 | Some(result) |
643 | } |
644 | }) |
645 | } |
646 | |
647 | /// Read an IPv4 address. |
648 | pub(super) fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> { |
649 | self.read_atomically(|p| { |
650 | let mut groups = [0; 4]; |
651 | |
652 | for (i, slot) in groups.iter_mut().enumerate() { |
653 | *slot = p.read_separator('.' , i, |p| { |
654 | // Disallow octal number in IP string. |
655 | // https://tools.ietf.org/html/rfc6943#section-3.1.1 |
656 | p.read_number(10, Some(3), false) |
657 | })?; |
658 | } |
659 | |
660 | Some(Ipv4Addr(groups)) |
661 | }) |
662 | } |
663 | |
664 | /// Read an IPv6 Address. |
665 | pub(super) fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> { |
666 | /// Read a chunk of an IPv6 address into `groups`. Returns the number |
667 | /// of groups read, along with a bool indicating if an embedded |
668 | /// trailing IPv4 address was read. Specifically, read a series of |
669 | /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional |
670 | /// trailing embedded IPv4 address. |
671 | fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { |
672 | let limit = groups.len(); |
673 | |
674 | for (i, slot) in groups.iter_mut().enumerate() { |
675 | // Try to read a trailing embedded IPv4 address. There must be |
676 | // at least two groups left. |
677 | if i < limit - 1 { |
678 | let ipv4 = p.read_separator(':' , i, |p| p.read_ipv4_addr()); |
679 | |
680 | if let Some(v4_addr) = ipv4 { |
681 | let [one, two, three, four] = v4_addr.0; |
682 | groups[i] = u16::from_be_bytes([one, two]); |
683 | groups[i + 1] = u16::from_be_bytes([three, four]); |
684 | return (i + 2, true); |
685 | } |
686 | } |
687 | |
688 | let group = p.read_separator(':' , i, |p| p.read_number(16, Some(4), true)); |
689 | |
690 | match group { |
691 | Some(g) => *slot = g, |
692 | None => return (i, false), |
693 | } |
694 | } |
695 | (groups.len(), false) |
696 | } |
697 | |
698 | self.read_atomically(|p| { |
699 | // Read the front part of the address; either the whole thing, or up |
700 | // to the first :: |
701 | let mut head = [0; 8]; |
702 | let (head_size, head_ipv4) = read_groups(p, &mut head); |
703 | |
704 | if head_size == 8 { |
705 | return Some(head.into()); |
706 | } |
707 | |
708 | // IPv4 part is not allowed before `::` |
709 | if head_ipv4 { |
710 | return None; |
711 | } |
712 | |
713 | // Read `::` if previous code parsed less than 8 groups. |
714 | // `::` indicates one or more groups of 16 bits of zeros. |
715 | p.read_given_char(':' )?; |
716 | p.read_given_char(':' )?; |
717 | |
718 | // Read the back part of the address. The :: must contain at least one |
719 | // set of zeroes, so our max length is 7. |
720 | let mut tail = [0; 7]; |
721 | let limit = 8 - (head_size + 1); |
722 | let (tail_size, _) = read_groups(p, &mut tail[..limit]); |
723 | |
724 | // Concat the head and tail of the IP address |
725 | head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); |
726 | |
727 | Some(head.into()) |
728 | }) |
729 | } |
730 | } |
731 | |
732 | trait ReadNumberHelper: Sized { |
733 | const ZERO: Self; |
734 | fn checked_mul(&self, other: u32) -> Option<Self>; |
735 | fn checked_add(&self, other: u32) -> Option<Self>; |
736 | } |
737 | |
738 | macro_rules! impl_helper { |
739 | ($($t:ty)*) => ($(impl ReadNumberHelper for $t { |
740 | const ZERO: Self = 0; |
741 | #[inline] |
742 | fn checked_mul(&self, other: u32) -> Option<Self> { |
743 | Self::checked_mul(*self, other.try_into().ok()?) |
744 | } |
745 | #[inline] |
746 | fn checked_add(&self, other: u32) -> Option<Self> { |
747 | Self::checked_add(*self, other.try_into().ok()?) |
748 | } |
749 | })*) |
750 | } |
751 | |
752 | impl_helper! { u8 u16 u32 } |
753 | |
754 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
755 | pub(super) enum AddrKind { |
756 | Ipv4, |
757 | Ipv6, |
758 | } |
759 | } |
760 | |
761 | use parser::{AddrKind, Parser}; |
762 | |
763 | /// Failure to parse an IP address |
764 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
765 | pub struct AddrParseError(AddrKind); |
766 | |
767 | impl core::fmt::Display for AddrParseError { |
768 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
769 | f.write_str(data:match self.0 { |
770 | AddrKind::Ipv4 => "invalid IPv4 address syntax" , |
771 | AddrKind::Ipv6 => "invalid IPv6 address syntax" , |
772 | }) |
773 | } |
774 | } |
775 | |
776 | #[cfg (feature = "std" )] |
777 | impl ::std::error::Error for AddrParseError {} |
778 | |
779 | #[cfg (test)] |
780 | mod tests { |
781 | use super::*; |
782 | #[cfg (feature = "alloc" )] |
783 | use alloc::format; |
784 | |
785 | #[cfg (feature = "alloc" )] |
786 | static TESTS: &[(&str, bool)] = &[ |
787 | ("" , false), |
788 | ("localhost" , true), |
789 | ("LOCALHOST" , true), |
790 | (".localhost" , false), |
791 | ("..localhost" , false), |
792 | ("1.2.3.4" , false), |
793 | ("127.0.0.1" , false), |
794 | ("absolute." , true), |
795 | ("absolute.." , false), |
796 | ("multiple.labels.absolute." , true), |
797 | ("foo.bar.com" , true), |
798 | ("infix-hyphen-allowed.com" , true), |
799 | ("-prefixhypheninvalid.com" , false), |
800 | ("suffixhypheninvalid--" , false), |
801 | ("suffixhypheninvalid-.com" , false), |
802 | ("foo.lastlabelendswithhyphen-" , false), |
803 | ("infix_underscore_allowed.com" , true), |
804 | ("_prefixunderscorevalid.com" , true), |
805 | ("labelendswithnumber1.bar.com" , true), |
806 | ("xn--bcher-kva.example" , true), |
807 | ( |
808 | "sixtythreesixtythreesixtythreesixtythreesixtythreesixtythreesix.com" , |
809 | true, |
810 | ), |
811 | ( |
812 | "sixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfours.com" , |
813 | false, |
814 | ), |
815 | ( |
816 | "012345678901234567890123456789012345678901234567890123456789012.com" , |
817 | true, |
818 | ), |
819 | ( |
820 | "0123456789012345678901234567890123456789012345678901234567890123.com" , |
821 | false, |
822 | ), |
823 | ( |
824 | "01234567890123456789012345678901234567890123456789012345678901-.com" , |
825 | false, |
826 | ), |
827 | ( |
828 | "012345678901234567890123456789012345678901234567890123456789012-.com" , |
829 | false, |
830 | ), |
831 | ("numeric-only-final-label.1" , false), |
832 | ("numeric-only-final-label.absolute.1." , false), |
833 | ("1starts-with-number.com" , true), |
834 | ("1Starts-with-number.com" , true), |
835 | ("1.2.3.4.com" , true), |
836 | ("123.numeric-only-first-label" , true), |
837 | ("a123b.com" , true), |
838 | ("numeric-only-middle-label.4.com" , true), |
839 | ("1000-sans.badssl.com" , true), |
840 | ("twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfi" , true), |
841 | ("twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourc" , false), |
842 | ]; |
843 | |
844 | #[cfg (feature = "alloc" )] |
845 | #[test ] |
846 | fn test_validation() { |
847 | for (input, expected) in TESTS { |
848 | #[cfg (feature = "std" )] |
849 | println!("test: {:?} expected valid? {:?}" , input, expected); |
850 | let name_ref = DnsName::try_from(*input); |
851 | assert_eq!(*expected, name_ref.is_ok()); |
852 | let name = DnsName::try_from(input.to_string()); |
853 | assert_eq!(*expected, name.is_ok()); |
854 | } |
855 | } |
856 | |
857 | #[cfg (feature = "alloc" )] |
858 | #[test ] |
859 | fn error_is_debug() { |
860 | assert_eq!(format!("{:?}" , InvalidDnsNameError), "InvalidDnsNameError" ); |
861 | } |
862 | |
863 | #[cfg (feature = "alloc" )] |
864 | #[test ] |
865 | fn error_is_display() { |
866 | assert_eq!(format!("{}" , InvalidDnsNameError), "invalid dns name" ); |
867 | } |
868 | |
869 | #[cfg (feature = "alloc" )] |
870 | #[test ] |
871 | fn dns_name_is_debug() { |
872 | let example = DnsName::try_from("example.com" .to_string()).unwrap(); |
873 | assert_eq!(format!("{:?}" , example), "DnsName( \"example.com \")" ); |
874 | } |
875 | |
876 | #[cfg (feature = "alloc" )] |
877 | #[test ] |
878 | fn dns_name_traits() { |
879 | let example = DnsName::try_from("example.com" .to_string()).unwrap(); |
880 | assert_eq!(example, example); // PartialEq |
881 | |
882 | #[cfg (feature = "std" )] |
883 | { |
884 | use std::collections::HashSet; |
885 | let mut h = HashSet::<DnsName>::new(); |
886 | h.insert(example); |
887 | } |
888 | } |
889 | |
890 | #[cfg (feature = "alloc" )] |
891 | #[test ] |
892 | fn try_from_ascii_rejects_bad_utf8() { |
893 | assert_eq!( |
894 | format!("{:?}" , DnsName::try_from(&b" \x80" [..])), |
895 | "Err(InvalidDnsNameError)" |
896 | ); |
897 | } |
898 | |
899 | const fn ipv4_address( |
900 | ip_address: &str, |
901 | octets: [u8; 4], |
902 | ) -> (&str, Result<Ipv4Addr, AddrParseError>) { |
903 | (ip_address, Ok(Ipv4Addr(octets))) |
904 | } |
905 | |
906 | const IPV4_ADDRESSES: &[(&str, Result<Ipv4Addr, AddrParseError>)] = &[ |
907 | // Valid IPv4 addresses |
908 | ipv4_address("0.0.0.0" , [0, 0, 0, 0]), |
909 | ipv4_address("1.1.1.1" , [1, 1, 1, 1]), |
910 | ipv4_address("205.0.0.0" , [205, 0, 0, 0]), |
911 | ipv4_address("0.205.0.0" , [0, 205, 0, 0]), |
912 | ipv4_address("0.0.205.0" , [0, 0, 205, 0]), |
913 | ipv4_address("0.0.0.205" , [0, 0, 0, 205]), |
914 | ipv4_address("0.0.0.20" , [0, 0, 0, 20]), |
915 | // Invalid IPv4 addresses |
916 | ("" , Err(AddrParseError(AddrKind::Ipv4))), |
917 | ("..." , Err(AddrParseError(AddrKind::Ipv4))), |
918 | (".0.0.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
919 | ("0.0.0.0." , Err(AddrParseError(AddrKind::Ipv4))), |
920 | ("0.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
921 | ("0.0.0." , Err(AddrParseError(AddrKind::Ipv4))), |
922 | ("256.0.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
923 | ("0.256.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
924 | ("0.0.256.0" , Err(AddrParseError(AddrKind::Ipv4))), |
925 | ("0.0.0.256" , Err(AddrParseError(AddrKind::Ipv4))), |
926 | ("1..1.1.1" , Err(AddrParseError(AddrKind::Ipv4))), |
927 | ("1.1..1.1" , Err(AddrParseError(AddrKind::Ipv4))), |
928 | ("1.1.1..1" , Err(AddrParseError(AddrKind::Ipv4))), |
929 | ("025.0.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
930 | ("0.025.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
931 | ("0.0.025.0" , Err(AddrParseError(AddrKind::Ipv4))), |
932 | ("0.0.0.025" , Err(AddrParseError(AddrKind::Ipv4))), |
933 | ("1234.0.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
934 | ("0.1234.0.0" , Err(AddrParseError(AddrKind::Ipv4))), |
935 | ("0.0.1234.0" , Err(AddrParseError(AddrKind::Ipv4))), |
936 | ("0.0.0.1234" , Err(AddrParseError(AddrKind::Ipv4))), |
937 | ]; |
938 | |
939 | #[test ] |
940 | fn parse_ipv4_address_test() { |
941 | for &(ip_address, expected_result) in IPV4_ADDRESSES { |
942 | assert_eq!(Ipv4Addr::try_from(ip_address), expected_result); |
943 | } |
944 | } |
945 | |
946 | const fn ipv6_address( |
947 | ip_address: &str, |
948 | octets: [u8; 16], |
949 | ) -> (&str, Result<Ipv6Addr, AddrParseError>) { |
950 | (ip_address, Ok(Ipv6Addr(octets))) |
951 | } |
952 | |
953 | const IPV6_ADDRESSES: &[(&str, Result<Ipv6Addr, AddrParseError>)] = &[ |
954 | // Valid IPv6 addresses |
955 | ipv6_address( |
956 | "2a05:d018:076c:b685:e8ab:afd3:af51:3aed" , |
957 | [ |
958 | 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, |
959 | 0x3a, 0xed, |
960 | ], |
961 | ), |
962 | ipv6_address( |
963 | "2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED" , |
964 | [ |
965 | 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, |
966 | 0x3a, 0xed, |
967 | ], |
968 | ), |
969 | ipv6_address( |
970 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
971 | [ |
972 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
973 | 0xff, 0xff, |
974 | ], |
975 | ), |
976 | ipv6_address( |
977 | "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF" , |
978 | [ |
979 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
980 | 0xff, 0xff, |
981 | ], |
982 | ), |
983 | ipv6_address( |
984 | "FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
985 | [ |
986 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
987 | 0xff, 0xff, |
988 | ], |
989 | ), |
990 | // Wrong hexadecimal characters on different positions |
991 | ( |
992 | "ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
993 | Err(AddrParseError(AddrKind::Ipv6)), |
994 | ), |
995 | ( |
996 | "ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff" , |
997 | Err(AddrParseError(AddrKind::Ipv6)), |
998 | ), |
999 | ( |
1000 | "ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff" , |
1001 | Err(AddrParseError(AddrKind::Ipv6)), |
1002 | ), |
1003 | ( |
1004 | "ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff" , |
1005 | Err(AddrParseError(AddrKind::Ipv6)), |
1006 | ), |
1007 | ( |
1008 | "ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff" , |
1009 | Err(AddrParseError(AddrKind::Ipv6)), |
1010 | ), |
1011 | ( |
1012 | "ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff" , |
1013 | Err(AddrParseError(AddrKind::Ipv6)), |
1014 | ), |
1015 | ( |
1016 | "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff" , |
1017 | Err(AddrParseError(AddrKind::Ipv6)), |
1018 | ), |
1019 | ( |
1020 | "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg" , |
1021 | Err(AddrParseError(AddrKind::Ipv6)), |
1022 | ), |
1023 | // Wrong colons on uncompressed addresses |
1024 | ( |
1025 | ":ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
1026 | Err(AddrParseError(AddrKind::Ipv6)), |
1027 | ), |
1028 | ( |
1029 | "ffff::ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
1030 | Err(AddrParseError(AddrKind::Ipv6)), |
1031 | ), |
1032 | ( |
1033 | "ffff:ffff::ffff:ffff:ffff:ffff:ffff:ffff" , |
1034 | Err(AddrParseError(AddrKind::Ipv6)), |
1035 | ), |
1036 | ( |
1037 | "ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff" , |
1038 | Err(AddrParseError(AddrKind::Ipv6)), |
1039 | ), |
1040 | ( |
1041 | "ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff" , |
1042 | Err(AddrParseError(AddrKind::Ipv6)), |
1043 | ), |
1044 | ( |
1045 | "ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff" , |
1046 | Err(AddrParseError(AddrKind::Ipv6)), |
1047 | ), |
1048 | ( |
1049 | "ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff" , |
1050 | Err(AddrParseError(AddrKind::Ipv6)), |
1051 | ), |
1052 | ( |
1053 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff" , |
1054 | Err(AddrParseError(AddrKind::Ipv6)), |
1055 | ), |
1056 | // More colons than allowed |
1057 | ( |
1058 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:" , |
1059 | Err(AddrParseError(AddrKind::Ipv6)), |
1060 | ), |
1061 | ( |
1062 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" , |
1063 | Err(AddrParseError(AddrKind::Ipv6)), |
1064 | ), |
1065 | // v Invalid hexadecimal |
1066 | ( |
1067 | "ga05:d018:076c:b685:e8ab:afd3:af51:3aed" , |
1068 | Err(AddrParseError(AddrKind::Ipv6)), |
1069 | ), |
1070 | // Cannot start with colon |
1071 | ( |
1072 | ":a05:d018:076c:b685:e8ab:afd3:af51:3aed" , |
1073 | Err(AddrParseError(AddrKind::Ipv6)), |
1074 | ), |
1075 | // Cannot end with colon |
1076 | ( |
1077 | "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:" , |
1078 | Err(AddrParseError(AddrKind::Ipv6)), |
1079 | ), |
1080 | // Cannot have more than seven colons |
1081 | ( |
1082 | "2a05:d018:076c:b685:e8ab:afd3:af51:3a::" , |
1083 | Err(AddrParseError(AddrKind::Ipv6)), |
1084 | ), |
1085 | // Cannot contain two colons in a row |
1086 | ( |
1087 | "2a05::018:076c:b685:e8ab:afd3:af51:3aed" , |
1088 | Err(AddrParseError(AddrKind::Ipv6)), |
1089 | ), |
1090 | // v Textual block size is longer |
1091 | ( |
1092 | "2a056:d018:076c:b685:e8ab:afd3:af51:3ae" , |
1093 | Err(AddrParseError(AddrKind::Ipv6)), |
1094 | ), |
1095 | // v Textual block size is shorter |
1096 | ( |
1097 | "2a0:d018:076c:b685:e8ab:afd3:af51:3aed " , |
1098 | Err(AddrParseError(AddrKind::Ipv6)), |
1099 | ), |
1100 | // Shorter IPv6 address |
1101 | ( |
1102 | "d018:076c:b685:e8ab:afd3:af51:3aed" , |
1103 | Err(AddrParseError(AddrKind::Ipv6)), |
1104 | ), |
1105 | // Longer IPv6 address |
1106 | ( |
1107 | "2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed" , |
1108 | Err(AddrParseError(AddrKind::Ipv6)), |
1109 | ), |
1110 | ]; |
1111 | |
1112 | #[test ] |
1113 | fn parse_ipv6_address_test() { |
1114 | for &(ip_address, expected_result) in IPV6_ADDRESSES { |
1115 | assert_eq!(Ipv6Addr::try_from(ip_address), expected_result); |
1116 | } |
1117 | } |
1118 | |
1119 | #[test ] |
1120 | fn try_from_ascii_ip_address_test() { |
1121 | const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[ |
1122 | // Valid IPv4 addresses |
1123 | ("127.0.0.1" , Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))), |
1124 | // Invalid IPv4 addresses |
1125 | ( |
1126 | // Ends with a dot; misses one octet |
1127 | "127.0.0." , |
1128 | Err(AddrParseError(AddrKind::Ipv6)), |
1129 | ), |
1130 | // Valid IPv6 addresses |
1131 | ( |
1132 | "0000:0000:0000:0000:0000:0000:0000:0001" , |
1133 | Ok(IpAddr::V6(Ipv6Addr([ |
1134 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
1135 | ]))), |
1136 | ), |
1137 | // Something else |
1138 | ( |
1139 | // A hostname |
1140 | "example.com" , |
1141 | Err(AddrParseError(AddrKind::Ipv6)), |
1142 | ), |
1143 | ]; |
1144 | for &(ip_address, expected_result) in IP_ADDRESSES { |
1145 | assert_eq!(IpAddr::try_from(ip_address), expected_result) |
1146 | } |
1147 | } |
1148 | |
1149 | #[test ] |
1150 | fn try_from_ascii_str_ip_address_test() { |
1151 | const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[ |
1152 | // Valid IPv4 addresses |
1153 | ("127.0.0.1" , Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))), |
1154 | // Invalid IPv4 addresses |
1155 | ( |
1156 | // Ends with a dot; misses one octet |
1157 | "127.0.0." , |
1158 | Err(AddrParseError(AddrKind::Ipv6)), |
1159 | ), |
1160 | // Valid IPv6 addresses |
1161 | ( |
1162 | "0000:0000:0000:0000:0000:0000:0000:0001" , |
1163 | Ok(IpAddr::V6(Ipv6Addr([ |
1164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
1165 | ]))), |
1166 | ), |
1167 | // Something else |
1168 | ( |
1169 | // A hostname |
1170 | "example.com" , |
1171 | Err(AddrParseError(AddrKind::Ipv6)), |
1172 | ), |
1173 | ]; |
1174 | for &(ip_address, expected_result) in IP_ADDRESSES { |
1175 | assert_eq!(IpAddr::try_from(ip_address), expected_result) |
1176 | } |
1177 | } |
1178 | |
1179 | #[test ] |
1180 | #[cfg (feature = "std" )] |
1181 | fn to_str() { |
1182 | let domain_str = "example.com" ; |
1183 | let domain_servername = ServerName::try_from(domain_str).unwrap(); |
1184 | assert_eq!(domain_str, domain_servername.to_str()); |
1185 | |
1186 | let ipv4_str = "127.0.0.1" ; |
1187 | let ipv4_servername = ServerName::try_from("127.0.0.1" ).unwrap(); |
1188 | assert_eq!(ipv4_str, ipv4_servername.to_str()); |
1189 | |
1190 | let ipv6_str = "::1" ; |
1191 | let ipv6_servername = ServerName::try_from(ipv6_str).unwrap(); |
1192 | assert_eq!("::1" , ipv6_servername.to_str()); |
1193 | } |
1194 | } |
1195 | |