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 LabelState {
319 Start,
321 HasLetter,
323 AllNumeric,
325 EndsInHyphen,
327 }
328
329 const MAX_LABEL_LENGTH: usize = 63;
331
332 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 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 match state {
386 LabelState::AllNumeric => Err(InvalidDnsNameError),
387 _ => Ok(()),
388 }
389}
390
391#[allow(clippy::exhaustive_enums)]
397#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
398pub enum IpAddr {
399 V4(Ipv4Addr),
401 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#[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 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#[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 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 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
557mod parser {
561 use super::{AddrParseError, Ipv4Addr, Ipv6Addr};
562
563 pub(super) struct Parser<'a> {
564 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 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 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 fn peek_char(&self) -> Option<char> {
602 self.state.first().map(|&b| char::from(b))
603 }
604
605 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 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 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 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 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 p.read_number(10, Some(3), false)
681 })?;
682 }
683
684 Some(Ipv4Addr(groups))
685 })
686 }
687
688 pub(super) fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
690 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 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 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 if head_ipv4 {
734 return None;
735 }
736
737 p.read_given_char(':')?;
740 p.read_given_char(':')?;
741
742 let mut tail = [0; 7];
745 let limit = 8 - (head_size + 1);
746 let (tail_size, _) = read_groups(p, &mut tail[..limit]);
747
748 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#[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); #[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 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 ("", 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 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 (
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 (
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 (
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 (
1097 "ga05:d018:076c:b685:e8ab:afd3:af51:3aed",
1098 Err(AddrParseError(AddrKind::Ipv6)),
1099 ),
1100 (
1102 ":a05:d018:076c:b685:e8ab:afd3:af51:3aed",
1103 Err(AddrParseError(AddrKind::Ipv6)),
1104 ),
1105 (
1107 "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:",
1108 Err(AddrParseError(AddrKind::Ipv6)),
1109 ),
1110 (
1112 "2a05:d018:076c:b685:e8ab:afd3:af51:3a::",
1113 Err(AddrParseError(AddrKind::Ipv6)),
1114 ),
1115 (
1117 "2a05::018:076c:b685:e8ab:afd3:af51:3aed",
1118 Err(AddrParseError(AddrKind::Ipv6)),
1119 ),
1120 (
1122 "2a056:d018:076c:b685:e8ab:afd3:af51:3ae",
1123 Err(AddrParseError(AddrKind::Ipv6)),
1124 ),
1125 (
1127 "2a0:d018:076c:b685:e8ab:afd3:af51:3aed ",
1128 Err(AddrParseError(AddrKind::Ipv6)),
1129 ),
1130 (
1132 "d018:076c:b685:e8ab:afd3:af51:3aed",
1133 Err(AddrParseError(AddrKind::Ipv6)),
1134 ),
1135 (
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 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1154 (
1156 "127.0.0.",
1158 Err(AddrParseError(AddrKind::Ipv6)),
1159 ),
1160 (
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 (
1169 "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 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1184 (
1186 "127.0.0.",
1188 Err(AddrParseError(AddrKind::Ipv6)),
1189 ),
1190 (
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 (
1199 "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}