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