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