1#[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#[non_exhaustive]
46#[derive(Clone, Eq, Hash, PartialEq)]
47pub enum ServerName<'a> {
48 DnsName(DnsName<'a>),
52
53 IpAddress(IpAddr),
56}
57
58impl ServerName<'_> {
59 #[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 #[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
116impl<'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#[derive(Clone, Debug, Eq, Hash, PartialEq)]
177pub struct DnsName<'a>(DnsNameInner<'a>);
178
179impl<'a> DnsName<'a> {
180 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 #[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 #[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 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#[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 State {
319 Start,
320 Next,
321 NumericOnly { len: usize },
322 NextAfterNumericOnly,
323 Subsequent { len: usize },
324 Hyphen { len: usize },
325 }
326
327 use State::*;
328 let mut state = Start;
329
330 const MAX_LABEL_LENGTH: usize = 63;
332
333 const MAX_NAME_LENGTH: usize = 253;
335
336 if input.len() > MAX_NAME_LENGTH {
337 return Err(InvalidDnsNameError);
338 }
339
340 let mut idx = 0;
341 while idx < input.len() {
342 let ch = input[idx];
343 state = match (state, ch) {
344 (Start | Next | NextAfterNumericOnly | Hyphen { .. }, b'.') => {
345 return Err(InvalidDnsNameError);
346 }
347 (Subsequent { .. }, b'.') => Next,
348 (NumericOnly { .. }, b'.') => NextAfterNumericOnly,
349 (Subsequent { len } | NumericOnly { len } | Hyphen { len }, _)
350 if len >= MAX_LABEL_LENGTH =>
351 {
352 return Err(InvalidDnsNameError);
353 }
354 (Start | Next | NextAfterNumericOnly, b'0'..=b'9') => NumericOnly { len: 1 },
355 (NumericOnly { len }, b'0'..=b'9') => NumericOnly { len: len + 1 },
356 (Start | Next | NextAfterNumericOnly, b'a'..=b'z' | b'A'..=b'Z' | b'_') => {
357 Subsequent { len: 1 }
358 }
359 (Subsequent { len } | NumericOnly { len } | Hyphen { len }, b'-') => {
360 Hyphen { len: len + 1 }
361 }
362 (
363 Subsequent { len } | NumericOnly { len } | Hyphen { len },
364 b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'0'..=b'9',
365 ) => Subsequent { len: len + 1 },
366 _ => return Err(InvalidDnsNameError),
367 };
368 idx += 1;
369 }
370
371 if matches!(
372 state,
373 Start | Hyphen { .. } | NumericOnly { .. } | NextAfterNumericOnly
374 ) {
375 return Err(InvalidDnsNameError);
376 }
377
378 Ok(())
379}
380
381#[allow(clippy::exhaustive_enums)]
387#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
388pub enum IpAddr {
389 V4(Ipv4Addr),
391 V6(Ipv6Addr),
393}
394
395impl TryFrom<&str> for IpAddr {
396 type Error = AddrParseError;
397
398 fn try_from(value: &str) -> Result<Self, Self::Error> {
399 match Ipv4Addr::try_from(value) {
400 Ok(v4) => Ok(Self::V4(v4)),
401 Err(_) => match Ipv6Addr::try_from(value) {
402 Ok(v6) => Ok(Self::V6(v6)),
403 Err(e) => Err(e),
404 },
405 }
406 }
407}
408
409#[cfg(feature = "std")]
410impl From<std::net::IpAddr> for IpAddr {
411 fn from(addr: std::net::IpAddr) -> Self {
412 match addr {
413 std::net::IpAddr::V4(v4) => Self::V4(v4.into()),
414 std::net::IpAddr::V6(v6) => Self::V6(v6.into()),
415 }
416 }
417}
418
419#[cfg(feature = "std")]
420impl From<IpAddr> for std::net::IpAddr {
421 fn from(value: IpAddr) -> Self {
422 match value {
423 IpAddr::V4(v4) => Self::from(std::net::Ipv4Addr::from(v4)),
424 IpAddr::V6(v6) => Self::from(std::net::Ipv6Addr::from(v6)),
425 }
426 }
427}
428
429#[cfg(feature = "std")]
430impl From<std::net::Ipv4Addr> for IpAddr {
431 fn from(v4: std::net::Ipv4Addr) -> Self {
432 Self::V4(v4.into())
433 }
434}
435
436#[cfg(feature = "std")]
437impl From<std::net::Ipv6Addr> for IpAddr {
438 fn from(v6: std::net::Ipv6Addr) -> Self {
439 Self::V6(v6.into())
440 }
441}
442
443#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
449pub struct Ipv4Addr([u8; 4]);
450
451impl From<[u8; 4]> for Ipv4Addr {
452 fn from(value: [u8; 4]) -> Self {
453 Self(value)
454 }
455}
456
457impl TryFrom<&str> for Ipv4Addr {
458 type Error = AddrParseError;
459
460 fn try_from(value: &str) -> Result<Self, Self::Error> {
461 if value.len() > 15 {
463 Err(AddrParseError(AddrKind::Ipv4))
464 } else {
465 Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
466 }
467 }
468}
469
470#[cfg(feature = "std")]
471impl From<std::net::Ipv4Addr> for Ipv4Addr {
472 fn from(addr: std::net::Ipv4Addr) -> Self {
473 Self(addr.octets())
474 }
475}
476
477#[cfg(feature = "std")]
478impl From<Ipv4Addr> for std::net::Ipv4Addr {
479 fn from(value: Ipv4Addr) -> Self {
480 Self::from(value.0)
481 }
482}
483
484impl AsRef<[u8; 4]> for Ipv4Addr {
485 fn as_ref(&self) -> &[u8; 4] {
486 &self.0
487 }
488}
489
490#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
496pub struct Ipv6Addr([u8; 16]);
497
498impl TryFrom<&str> for Ipv6Addr {
499 type Error = AddrParseError;
500
501 fn try_from(value: &str) -> Result<Self, Self::Error> {
502 Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
503 }
504}
505
506impl From<[u16; 8]> for Ipv6Addr {
507 fn from(value: [u16; 8]) -> Self {
508 let addr16 = [
510 value[0].to_be(),
511 value[1].to_be(),
512 value[2].to_be(),
513 value[3].to_be(),
514 value[4].to_be(),
515 value[5].to_be(),
516 value[6].to_be(),
517 value[7].to_be(),
518 ];
519 Self(
520 unsafe { mem::transmute::<[u16; 8], [u8; 16]>(addr16) },
523 )
524 }
525}
526
527#[cfg(feature = "std")]
528impl From<std::net::Ipv6Addr> for Ipv6Addr {
529 fn from(addr: std::net::Ipv6Addr) -> Self {
530 Self(addr.octets())
531 }
532}
533
534#[cfg(feature = "std")]
535impl From<Ipv6Addr> for std::net::Ipv6Addr {
536 fn from(value: Ipv6Addr) -> Self {
537 Self::from(value.0)
538 }
539}
540
541impl AsRef<[u8; 16]> for Ipv6Addr {
542 fn as_ref(&self) -> &[u8; 16] {
543 &self.0
544 }
545}
546
547mod parser {
551 use super::{AddrParseError, Ipv4Addr, Ipv6Addr};
552
553 pub(super) struct Parser<'a> {
554 state: &'a [u8],
556 }
557
558 impl<'a> Parser<'a> {
559 pub(super) const fn new(input: &'a [u8]) -> Self {
560 Parser { state: input }
561 }
562
563 fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
565 where
566 F: FnOnce(&mut Parser<'_>) -> Option<T>,
567 {
568 let state = self.state;
569 let result = inner(self);
570 if result.is_none() {
571 self.state = state;
572 }
573 result
574 }
575
576 pub(super) fn parse_with<T, F>(
579 &mut self,
580 inner: F,
581 kind: AddrKind,
582 ) -> Result<T, AddrParseError>
583 where
584 F: FnOnce(&mut Parser<'_>) -> Option<T>,
585 {
586 let result = inner(self);
587 if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind))
588 }
589
590 fn peek_char(&self) -> Option<char> {
592 self.state.first().map(|&b| char::from(b))
593 }
594
595 fn read_char(&mut self) -> Option<char> {
597 self.state.split_first().map(|(&b, tail)| {
598 self.state = tail;
599 char::from(b)
600 })
601 }
602
603 #[must_use]
604 fn read_given_char(&mut self, target: char) -> Option<()> {
606 self.read_atomically(|p| {
607 p.read_char()
608 .and_then(|c| if c == target { Some(()) } else { None })
609 })
610 }
611
612 fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T>
617 where
618 F: FnOnce(&mut Parser<'_>) -> Option<T>,
619 {
620 self.read_atomically(move |p| {
621 if index > 0 {
622 p.read_given_char(sep)?;
623 }
624 inner(p)
625 })
626 }
627
628 fn read_number<T: ReadNumberHelper>(
632 &mut self,
633 radix: u32,
634 max_digits: Option<usize>,
635 allow_zero_prefix: bool,
636 ) -> Option<T> {
637 self.read_atomically(move |p| {
638 let mut result = T::ZERO;
639 let mut digit_count = 0;
640 let has_leading_zero = p.peek_char() == Some('0');
641
642 while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
643 result = result.checked_mul(radix)?;
644 result = result.checked_add(digit)?;
645 digit_count += 1;
646 if let Some(max_digits) = max_digits {
647 if digit_count > max_digits {
648 return None;
649 }
650 }
651 }
652
653 if digit_count == 0 || (!allow_zero_prefix && has_leading_zero && digit_count > 1) {
654 None
655 } else {
656 Some(result)
657 }
658 })
659 }
660
661 pub(super) fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
663 self.read_atomically(|p| {
664 let mut groups = [0; 4];
665
666 for (i, slot) in groups.iter_mut().enumerate() {
667 *slot = p.read_separator('.', i, |p| {
668 p.read_number(10, Some(3), false)
671 })?;
672 }
673
674 Some(Ipv4Addr(groups))
675 })
676 }
677
678 pub(super) fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
680 fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
686 let limit = groups.len();
687
688 for (i, slot) in groups.iter_mut().enumerate() {
689 if i < limit - 1 {
692 let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());
693
694 if let Some(v4_addr) = ipv4 {
695 let [one, two, three, four] = v4_addr.0;
696 groups[i] = u16::from_be_bytes([one, two]);
697 groups[i + 1] = u16::from_be_bytes([three, four]);
698 return (i + 2, true);
699 }
700 }
701
702 let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));
703
704 match group {
705 Some(g) => *slot = g,
706 None => return (i, false),
707 }
708 }
709 (groups.len(), false)
710 }
711
712 self.read_atomically(|p| {
713 let mut head = [0; 8];
716 let (head_size, head_ipv4) = read_groups(p, &mut head);
717
718 if head_size == 8 {
719 return Some(head.into());
720 }
721
722 if head_ipv4 {
724 return None;
725 }
726
727 p.read_given_char(':')?;
730 p.read_given_char(':')?;
731
732 let mut tail = [0; 7];
735 let limit = 8 - (head_size + 1);
736 let (tail_size, _) = read_groups(p, &mut tail[..limit]);
737
738 head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]);
740
741 Some(head.into())
742 })
743 }
744 }
745
746 trait ReadNumberHelper: Sized {
747 const ZERO: Self;
748 fn checked_mul(&self, other: u32) -> Option<Self>;
749 fn checked_add(&self, other: u32) -> Option<Self>;
750 }
751
752 macro_rules! impl_helper {
753 ($($t:ty)*) => ($(impl ReadNumberHelper for $t {
754 const ZERO: Self = 0;
755 #[inline]
756 fn checked_mul(&self, other: u32) -> Option<Self> {
757 Self::checked_mul(*self, other.try_into().ok()?)
758 }
759 #[inline]
760 fn checked_add(&self, other: u32) -> Option<Self> {
761 Self::checked_add(*self, other.try_into().ok()?)
762 }
763 })*)
764 }
765
766 impl_helper! { u8 u16 u32 }
767
768 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
769 pub(super) enum AddrKind {
770 Ipv4,
771 Ipv6,
772 }
773}
774
775use parser::{AddrKind, Parser};
776
777#[derive(Debug, Clone, Copy, Eq, PartialEq)]
779pub struct AddrParseError(AddrKind);
780
781impl fmt::Display for AddrParseError {
782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
783 f.write_str(match self.0 {
784 AddrKind::Ipv4 => "invalid IPv4 address syntax",
785 AddrKind::Ipv6 => "invalid IPv6 address syntax",
786 })
787 }
788}
789
790#[cfg(feature = "std")]
791impl ::std::error::Error for AddrParseError {}
792
793#[cfg(test)]
794mod tests {
795 use super::*;
796 #[cfg(feature = "alloc")]
797 use alloc::format;
798
799 #[cfg(feature = "alloc")]
800 static TESTS: &[(&str, bool)] = &[
801 ("", false),
802 ("localhost", true),
803 ("LOCALHOST", true),
804 (".localhost", false),
805 ("..localhost", false),
806 ("1.2.3.4", false),
807 ("127.0.0.1", false),
808 ("absolute.", true),
809 ("absolute..", false),
810 ("multiple.labels.absolute.", true),
811 ("foo.bar.com", true),
812 ("infix-hyphen-allowed.com", true),
813 ("-prefixhypheninvalid.com", false),
814 ("suffixhypheninvalid--", false),
815 ("suffixhypheninvalid-.com", false),
816 ("foo.lastlabelendswithhyphen-", false),
817 ("infix_underscore_allowed.com", true),
818 ("_prefixunderscorevalid.com", true),
819 ("labelendswithnumber1.bar.com", true),
820 ("xn--bcher-kva.example", true),
821 (
822 "sixtythreesixtythreesixtythreesixtythreesixtythreesixtythreesix.com",
823 true,
824 ),
825 (
826 "sixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfours.com",
827 false,
828 ),
829 (
830 "012345678901234567890123456789012345678901234567890123456789012.com",
831 true,
832 ),
833 (
834 "0123456789012345678901234567890123456789012345678901234567890123.com",
835 false,
836 ),
837 (
838 "01234567890123456789012345678901234567890123456789012345678901-.com",
839 false,
840 ),
841 (
842 "012345678901234567890123456789012345678901234567890123456789012-.com",
843 false,
844 ),
845 ("numeric-only-final-label.1", false),
846 ("numeric-only-final-label.absolute.1.", false),
847 ("1starts-with-number.com", true),
848 ("1Starts-with-number.com", true),
849 ("1.2.3.4.com", true),
850 ("123.numeric-only-first-label", true),
851 ("a123b.com", true),
852 ("numeric-only-middle-label.4.com", true),
853 ("1000-sans.badssl.com", true),
854 (
855 "twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfi",
856 true,
857 ),
858 (
859 "twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourc",
860 false,
861 ),
862 ];
863
864 #[cfg(feature = "alloc")]
865 #[test]
866 fn test_validation() {
867 for (input, expected) in TESTS {
868 #[cfg(feature = "std")]
869 println!("test: {input:?} expected valid? {expected:?}");
870 let name_ref = DnsName::try_from(*input);
871 assert_eq!(*expected, name_ref.is_ok());
872 let name = DnsName::try_from(input.to_string());
873 assert_eq!(*expected, name.is_ok());
874 }
875 }
876
877 #[cfg(feature = "alloc")]
878 #[test]
879 fn error_is_debug() {
880 assert_eq!(format!("{InvalidDnsNameError:?}"), "InvalidDnsNameError");
881 }
882
883 #[cfg(feature = "alloc")]
884 #[test]
885 fn error_is_display() {
886 assert_eq!(format!("{InvalidDnsNameError}"), "invalid dns name");
887 }
888
889 #[cfg(feature = "alloc")]
890 #[test]
891 fn dns_name_is_debug() {
892 let example = DnsName::try_from("example.com".to_string()).unwrap();
893 assert_eq!(format!("{example:?}"), "DnsName(\"example.com\")");
894 }
895
896 #[cfg(feature = "alloc")]
897 #[test]
898 fn dns_name_traits() {
899 let example = DnsName::try_from("example.com".to_string()).unwrap();
900 assert_eq!(example, example); #[cfg(feature = "std")]
903 {
904 use std::collections::HashSet;
905 let mut h = HashSet::<DnsName<'_>>::new();
906 h.insert(example);
907 }
908 }
909
910 #[cfg(feature = "alloc")]
911 #[test]
912 fn try_from_ascii_rejects_bad_utf8() {
913 assert_eq!(
914 format!("{:?}", DnsName::try_from(&b"\x80"[..])),
915 "Err(InvalidDnsNameError)"
916 );
917 }
918
919 const fn ipv4_address(
920 ip_address: &str,
921 octets: [u8; 4],
922 ) -> (&str, Result<Ipv4Addr, AddrParseError>) {
923 (ip_address, Ok(Ipv4Addr(octets)))
924 }
925
926 const IPV4_ADDRESSES: &[(&str, Result<Ipv4Addr, AddrParseError>)] = &[
927 ipv4_address("0.0.0.0", [0, 0, 0, 0]),
929 ipv4_address("1.1.1.1", [1, 1, 1, 1]),
930 ipv4_address("205.0.0.0", [205, 0, 0, 0]),
931 ipv4_address("0.205.0.0", [0, 205, 0, 0]),
932 ipv4_address("0.0.205.0", [0, 0, 205, 0]),
933 ipv4_address("0.0.0.205", [0, 0, 0, 205]),
934 ipv4_address("0.0.0.20", [0, 0, 0, 20]),
935 ("", Err(AddrParseError(AddrKind::Ipv4))),
937 ("...", Err(AddrParseError(AddrKind::Ipv4))),
938 (".0.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
939 ("0.0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
940 ("0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
941 ("0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
942 ("256.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
943 ("0.256.0.0", Err(AddrParseError(AddrKind::Ipv4))),
944 ("0.0.256.0", Err(AddrParseError(AddrKind::Ipv4))),
945 ("0.0.0.256", Err(AddrParseError(AddrKind::Ipv4))),
946 ("1..1.1.1", Err(AddrParseError(AddrKind::Ipv4))),
947 ("1.1..1.1", Err(AddrParseError(AddrKind::Ipv4))),
948 ("1.1.1..1", Err(AddrParseError(AddrKind::Ipv4))),
949 ("025.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
950 ("0.025.0.0", Err(AddrParseError(AddrKind::Ipv4))),
951 ("0.0.025.0", Err(AddrParseError(AddrKind::Ipv4))),
952 ("0.0.0.025", Err(AddrParseError(AddrKind::Ipv4))),
953 ("1234.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
954 ("0.1234.0.0", Err(AddrParseError(AddrKind::Ipv4))),
955 ("0.0.1234.0", Err(AddrParseError(AddrKind::Ipv4))),
956 ("0.0.0.1234", Err(AddrParseError(AddrKind::Ipv4))),
957 ];
958
959 #[test]
960 fn parse_ipv4_address_test() {
961 for &(ip_address, expected_result) in IPV4_ADDRESSES {
962 assert_eq!(Ipv4Addr::try_from(ip_address), expected_result);
963 }
964 }
965
966 const fn ipv6_address(
967 ip_address: &str,
968 octets: [u8; 16],
969 ) -> (&str, Result<Ipv6Addr, AddrParseError>) {
970 (ip_address, Ok(Ipv6Addr(octets)))
971 }
972
973 const IPV6_ADDRESSES: &[(&str, Result<Ipv6Addr, AddrParseError>)] = &[
974 ipv6_address(
976 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed",
977 [
978 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
979 0x3a, 0xed,
980 ],
981 ),
982 ipv6_address(
983 "2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED",
984 [
985 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
986 0x3a, 0xed,
987 ],
988 ),
989 ipv6_address(
990 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
991 [
992 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
993 0xff, 0xff,
994 ],
995 ),
996 ipv6_address(
997 "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
998 [
999 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1000 0xff, 0xff,
1001 ],
1002 ),
1003 ipv6_address(
1004 "FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1005 [
1006 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1007 0xff, 0xff,
1008 ],
1009 ),
1010 (
1012 "ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1013 Err(AddrParseError(AddrKind::Ipv6)),
1014 ),
1015 (
1016 "ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff",
1017 Err(AddrParseError(AddrKind::Ipv6)),
1018 ),
1019 (
1020 "ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff",
1021 Err(AddrParseError(AddrKind::Ipv6)),
1022 ),
1023 (
1024 "ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff",
1025 Err(AddrParseError(AddrKind::Ipv6)),
1026 ),
1027 (
1028 "ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff",
1029 Err(AddrParseError(AddrKind::Ipv6)),
1030 ),
1031 (
1032 "ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff",
1033 Err(AddrParseError(AddrKind::Ipv6)),
1034 ),
1035 (
1036 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff",
1037 Err(AddrParseError(AddrKind::Ipv6)),
1038 ),
1039 (
1040 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg",
1041 Err(AddrParseError(AddrKind::Ipv6)),
1042 ),
1043 (
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 (
1057 "ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff",
1058 Err(AddrParseError(AddrKind::Ipv6)),
1059 ),
1060 (
1061 "ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff",
1062 Err(AddrParseError(AddrKind::Ipv6)),
1063 ),
1064 (
1065 "ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff",
1066 Err(AddrParseError(AddrKind::Ipv6)),
1067 ),
1068 (
1069 "ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff",
1070 Err(AddrParseError(AddrKind::Ipv6)),
1071 ),
1072 (
1073 "ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff",
1074 Err(AddrParseError(AddrKind::Ipv6)),
1075 ),
1076 (
1078 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:",
1079 Err(AddrParseError(AddrKind::Ipv6)),
1080 ),
1081 (
1082 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1083 Err(AddrParseError(AddrKind::Ipv6)),
1084 ),
1085 (
1087 "ga05:d018:076c:b685:e8ab:afd3:af51:3aed",
1088 Err(AddrParseError(AddrKind::Ipv6)),
1089 ),
1090 (
1092 ":a05:d018:076c:b685:e8ab:afd3:af51:3aed",
1093 Err(AddrParseError(AddrKind::Ipv6)),
1094 ),
1095 (
1097 "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:",
1098 Err(AddrParseError(AddrKind::Ipv6)),
1099 ),
1100 (
1102 "2a05:d018:076c:b685:e8ab:afd3:af51:3a::",
1103 Err(AddrParseError(AddrKind::Ipv6)),
1104 ),
1105 (
1107 "2a05::018:076c:b685:e8ab:afd3:af51:3aed",
1108 Err(AddrParseError(AddrKind::Ipv6)),
1109 ),
1110 (
1112 "2a056:d018:076c:b685:e8ab:afd3:af51:3ae",
1113 Err(AddrParseError(AddrKind::Ipv6)),
1114 ),
1115 (
1117 "2a0:d018:076c:b685:e8ab:afd3:af51:3aed ",
1118 Err(AddrParseError(AddrKind::Ipv6)),
1119 ),
1120 (
1122 "d018:076c:b685:e8ab:afd3:af51:3aed",
1123 Err(AddrParseError(AddrKind::Ipv6)),
1124 ),
1125 (
1127 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed",
1128 Err(AddrParseError(AddrKind::Ipv6)),
1129 ),
1130 ];
1131
1132 #[test]
1133 fn parse_ipv6_address_test() {
1134 for &(ip_address, expected_result) in IPV6_ADDRESSES {
1135 assert_eq!(Ipv6Addr::try_from(ip_address), expected_result);
1136 }
1137 }
1138
1139 #[test]
1140 fn try_from_ascii_ip_address_test() {
1141 const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[
1142 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1144 (
1146 "127.0.0.",
1148 Err(AddrParseError(AddrKind::Ipv6)),
1149 ),
1150 (
1152 "0000:0000:0000:0000:0000:0000:0000:0001",
1153 Ok(IpAddr::V6(Ipv6Addr([
1154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1155 ]))),
1156 ),
1157 (
1159 "example.com",
1161 Err(AddrParseError(AddrKind::Ipv6)),
1162 ),
1163 ];
1164 for &(ip_address, expected_result) in IP_ADDRESSES {
1165 assert_eq!(IpAddr::try_from(ip_address), expected_result)
1166 }
1167 }
1168
1169 #[test]
1170 fn try_from_ascii_str_ip_address_test() {
1171 const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[
1172 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1174 (
1176 "127.0.0.",
1178 Err(AddrParseError(AddrKind::Ipv6)),
1179 ),
1180 (
1182 "0000:0000:0000:0000:0000:0000:0000:0001",
1183 Ok(IpAddr::V6(Ipv6Addr([
1184 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1185 ]))),
1186 ),
1187 (
1189 "example.com",
1191 Err(AddrParseError(AddrKind::Ipv6)),
1192 ),
1193 ];
1194 for &(ip_address, expected_result) in IP_ADDRESSES {
1195 assert_eq!(IpAddr::try_from(ip_address), expected_result)
1196 }
1197 }
1198
1199 #[test]
1200 #[cfg(feature = "std")]
1201 fn to_str() {
1202 let domain_str = "example.com";
1203 let domain_servername = ServerName::try_from(domain_str).unwrap();
1204 assert_eq!(domain_str, domain_servername.to_str());
1205
1206 let ipv4_str = "127.0.0.1";
1207 let ipv4_servername = ServerName::try_from("127.0.0.1").unwrap();
1208 assert_eq!(ipv4_str, ipv4_servername.to_str());
1209
1210 let ipv6_str = "::1";
1211 let ipv6_servername = ServerName::try_from(ipv6_str).unwrap();
1212 assert_eq!("::1", ipv6_servername.to_str());
1213 }
1214}