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