rustls_pki_types/
server_name.rs

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