Skip to main content

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