1//! DNS name validation according to RFC1035, but with underscores allowed.
2
3#[cfg(all(feature = "alloc", feature = "std"))]
4use alloc::borrow::Cow;
5#[cfg(feature = "alloc")]
6use alloc::string::{String, ToString};
7use core::hash::{Hash, Hasher};
8use core::{fmt, mem, str};
9#[cfg(feature = "std")]
10use 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)]
47pub 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
58impl<'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
81impl<'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")]
91impl 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
105impl<'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.
117impl<'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)]
132pub struct DnsName<'a>(DnsNameInner<'a>);
133
134impl<'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")]
171impl 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
179impl<'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
188impl<'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
197impl<'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)]
208enum DnsNameInner<'a> {
209 Borrowed(&'a str),
210 #[cfg(feature = "alloc")]
211 Owned(String),
212}
213
214impl<'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
228impl<'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
240impl<'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)]
253pub struct InvalidDnsNameError;
254
255impl 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")]
262impl StdError for InvalidDnsNameError {}
263
264fn 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)]
331pub enum IpAddr {
332 /// An Ipv4 address.
333 V4(Ipv4Addr),
334 /// An Ipv6 address.
335 V6(Ipv6Addr),
336}
337
338impl 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")]
353impl 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")]
363impl 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)]
378pub struct Ipv4Addr([u8; 4]);
379
380impl 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")]
394impl 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")]
401impl From<Ipv4Addr> for std::net::Ipv4Addr {
402 fn from(value: Ipv4Addr) -> Self {
403 Self::from(value.0)
404 }
405}
406
407impl 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)]
419pub struct Ipv6Addr([u8; 16]);
420
421impl 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
429impl 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")]
451impl 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")]
458impl From<Ipv6Addr> for std::net::Ipv6Addr {
459 fn from(value: Ipv6Addr) -> Self {
460 Self::from(value.0)
461 }
462}
463
464impl 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
473mod 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
698use parser::{AddrKind, Parser};
699
700/// Failure to parse an IP address
701#[derive(Debug, Clone, Copy, Eq, PartialEq)]
702pub struct AddrParseError(AddrKind);
703
704impl 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")]
714impl ::std::error::Error for AddrParseError {}
715
716#[cfg(test)]
717mod 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