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
169#[derive(Clone, Debug, Eq, Hash, PartialEq)]
171pub struct DnsName<'a>(DnsNameInner<'a>);
172
173impl<'a> DnsName<'a> {
174 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 #[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 #[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
209#[cfg(feature = "alloc")]
210impl TryFrom<String> for DnsName<'static> {
211 type Error = InvalidDnsNameError;
212
213 fn try_from(value: String) -> Result<Self, Self::Error> {
214 Self::try_from_string(value).map_err(|_| InvalidDnsNameError)
215 }
216}
217
218impl<'a> TryFrom<&'a str> for DnsName<'a> {
219 type Error = InvalidDnsNameError;
220
221 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
222 validate(value.as_bytes())?;
223 Ok(Self(DnsNameInner::Borrowed(value)))
224 }
225}
226
227impl<'a> TryFrom<&'a [u8]> for DnsName<'a> {
228 type Error = InvalidDnsNameError;
229
230 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
231 validate(value)?;
232 Ok(Self(DnsNameInner::Borrowed(str::from_utf8(value).unwrap())))
233 }
234}
235
236impl AsRef<str> for DnsName<'_> {
237 fn as_ref(&self) -> &str {
238 match self {
239 Self(DnsNameInner::Borrowed(s)) => s,
240 #[cfg(feature = "alloc")]
241 Self(DnsNameInner::Owned(s)) => s.as_str(),
242 }
243 }
244}
245
246#[derive(Clone, Eq)]
247enum DnsNameInner<'a> {
248 Borrowed(&'a str),
249 #[cfg(feature = "alloc")]
250 Owned(String),
251}
252
253impl PartialEq<Self> for DnsNameInner<'_> {
254 fn eq(&self, other: &Self) -> bool {
255 match (self, other) {
256 (Self::Borrowed(s), Self::Borrowed(o)) => s.eq_ignore_ascii_case(o),
257 #[cfg(feature = "alloc")]
258 (Self::Borrowed(s), Self::Owned(o)) => s.eq_ignore_ascii_case(o.as_str()),
259 #[cfg(feature = "alloc")]
260 (Self::Owned(s), Self::Borrowed(o)) => s.eq_ignore_ascii_case(o),
261 #[cfg(feature = "alloc")]
262 (Self::Owned(s), Self::Owned(o)) => s.eq_ignore_ascii_case(o.as_str()),
263 }
264 }
265}
266
267impl Hash for DnsNameInner<'_> {
268 fn hash<H: Hasher>(&self, state: &mut H) {
269 let s = match self {
270 Self::Borrowed(s) => s,
271 #[cfg(feature = "alloc")]
272 Self::Owned(s) => s.as_str(),
273 };
274
275 s.chars().for_each(|c| c.to_ascii_lowercase().hash(state));
276 }
277}
278
279impl fmt::Debug for DnsNameInner<'_> {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 match self {
282 Self::Borrowed(s) => f.write_fmt(format_args!("{:?}", s)),
283 #[cfg(feature = "alloc")]
284 Self::Owned(s) => f.write_fmt(format_args!("{:?}", s)),
285 }
286 }
287}
288
289#[derive(Debug)]
292pub struct InvalidDnsNameError;
293
294impl fmt::Display for InvalidDnsNameError {
295 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296 f.write_str("invalid dns name")
297 }
298}
299
300#[cfg(feature = "std")]
301impl StdError for InvalidDnsNameError {}
302
303fn validate(input: &[u8]) -> Result<(), InvalidDnsNameError> {
304 enum State {
305 Start,
306 Next,
307 NumericOnly { len: usize },
308 NextAfterNumericOnly,
309 Subsequent { len: usize },
310 Hyphen { len: usize },
311 }
312
313 use State::*;
314 let mut state = Start;
315
316 const MAX_LABEL_LENGTH: usize = 63;
318
319 const MAX_NAME_LENGTH: usize = 253;
321
322 if input.len() > MAX_NAME_LENGTH {
323 return Err(InvalidDnsNameError);
324 }
325
326 for ch in input {
327 state = match (state, ch) {
328 (Start | Next | NextAfterNumericOnly | Hyphen { .. }, b'.') => {
329 return Err(InvalidDnsNameError)
330 }
331 (Subsequent { .. }, b'.') => Next,
332 (NumericOnly { .. }, b'.') => NextAfterNumericOnly,
333 (Subsequent { len } | NumericOnly { len } | Hyphen { len }, _)
334 if len >= MAX_LABEL_LENGTH =>
335 {
336 return Err(InvalidDnsNameError)
337 }
338 (Start | Next | NextAfterNumericOnly, b'0'..=b'9') => NumericOnly { len: 1 },
339 (NumericOnly { len }, b'0'..=b'9') => NumericOnly { len: len + 1 },
340 (Start | Next | NextAfterNumericOnly, b'a'..=b'z' | b'A'..=b'Z' | b'_') => {
341 Subsequent { len: 1 }
342 }
343 (Subsequent { len } | NumericOnly { len } | Hyphen { len }, b'-') => {
344 Hyphen { len: len + 1 }
345 }
346 (
347 Subsequent { len } | NumericOnly { len } | Hyphen { len },
348 b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'0'..=b'9',
349 ) => Subsequent { len: len + 1 },
350 _ => return Err(InvalidDnsNameError),
351 };
352 }
353
354 if matches!(
355 state,
356 Start | Hyphen { .. } | NumericOnly { .. } | NextAfterNumericOnly
357 ) {
358 return Err(InvalidDnsNameError);
359 }
360
361 Ok(())
362}
363
364#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
370pub enum IpAddr {
371 V4(Ipv4Addr),
373 V6(Ipv6Addr),
375}
376
377impl TryFrom<&str> for IpAddr {
378 type Error = AddrParseError;
379
380 fn try_from(value: &str) -> Result<Self, Self::Error> {
381 match Ipv4Addr::try_from(value) {
382 Ok(v4) => Ok(Self::V4(v4)),
383 Err(_) => match Ipv6Addr::try_from(value) {
384 Ok(v6) => Ok(Self::V6(v6)),
385 Err(e) => Err(e),
386 },
387 }
388 }
389}
390
391#[cfg(feature = "std")]
392impl From<std::net::IpAddr> for IpAddr {
393 fn from(addr: std::net::IpAddr) -> Self {
394 match addr {
395 std::net::IpAddr::V4(v4) => Self::V4(v4.into()),
396 std::net::IpAddr::V6(v6) => Self::V6(v6.into()),
397 }
398 }
399}
400
401#[cfg(feature = "std")]
402impl From<IpAddr> for std::net::IpAddr {
403 fn from(value: IpAddr) -> Self {
404 match value {
405 IpAddr::V4(v4) => Self::from(std::net::Ipv4Addr::from(v4)),
406 IpAddr::V6(v6) => Self::from(std::net::Ipv6Addr::from(v6)),
407 }
408 }
409}
410
411#[cfg(feature = "std")]
412impl From<std::net::Ipv4Addr> for IpAddr {
413 fn from(v4: std::net::Ipv4Addr) -> Self {
414 Self::V4(v4.into())
415 }
416}
417
418#[cfg(feature = "std")]
419impl From<std::net::Ipv6Addr> for IpAddr {
420 fn from(v6: std::net::Ipv6Addr) -> Self {
421 Self::V6(v6.into())
422 }
423}
424
425#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
431pub struct Ipv4Addr([u8; 4]);
432
433impl TryFrom<&str> for Ipv4Addr {
434 type Error = AddrParseError;
435
436 fn try_from(value: &str) -> Result<Self, Self::Error> {
437 if value.len() > 15 {
439 Err(AddrParseError(AddrKind::Ipv4))
440 } else {
441 Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
442 }
443 }
444}
445
446#[cfg(feature = "std")]
447impl From<std::net::Ipv4Addr> for Ipv4Addr {
448 fn from(addr: std::net::Ipv4Addr) -> Self {
449 Self(addr.octets())
450 }
451}
452
453#[cfg(feature = "std")]
454impl From<Ipv4Addr> for std::net::Ipv4Addr {
455 fn from(value: Ipv4Addr) -> Self {
456 Self::from(value.0)
457 }
458}
459
460impl AsRef<[u8; 4]> for Ipv4Addr {
461 fn as_ref(&self) -> &[u8; 4] {
462 &self.0
463 }
464}
465
466#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
472pub struct Ipv6Addr([u8; 16]);
473
474impl TryFrom<&str> for Ipv6Addr {
475 type Error = AddrParseError;
476
477 fn try_from(value: &str) -> Result<Self, Self::Error> {
478 Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
479 }
480}
481
482impl From<[u16; 8]> for Ipv6Addr {
483 fn from(value: [u16; 8]) -> Self {
484 let addr16 = [
486 value[0].to_be(),
487 value[1].to_be(),
488 value[2].to_be(),
489 value[3].to_be(),
490 value[4].to_be(),
491 value[5].to_be(),
492 value[6].to_be(),
493 value[7].to_be(),
494 ];
495 Self(
496 unsafe { mem::transmute::<[u16; 8], [u8; 16]>(addr16) },
499 )
500 }
501}
502
503#[cfg(feature = "std")]
504impl From<std::net::Ipv6Addr> for Ipv6Addr {
505 fn from(addr: std::net::Ipv6Addr) -> Self {
506 Self(addr.octets())
507 }
508}
509
510#[cfg(feature = "std")]
511impl From<Ipv6Addr> for std::net::Ipv6Addr {
512 fn from(value: Ipv6Addr) -> Self {
513 Self::from(value.0)
514 }
515}
516
517impl AsRef<[u8; 16]> for Ipv6Addr {
518 fn as_ref(&self) -> &[u8; 16] {
519 &self.0
520 }
521}
522
523mod parser {
527 use super::{AddrParseError, Ipv4Addr, Ipv6Addr};
528
529 pub(super) struct Parser<'a> {
530 state: &'a [u8],
532 }
533
534 impl<'a> Parser<'a> {
535 pub(super) fn new(input: &'a [u8]) -> Self {
536 Parser { state: input }
537 }
538
539 fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
541 where
542 F: FnOnce(&mut Parser<'_>) -> Option<T>,
543 {
544 let state = self.state;
545 let result = inner(self);
546 if result.is_none() {
547 self.state = state;
548 }
549 result
550 }
551
552 pub(super) fn parse_with<T, F>(
555 &mut self,
556 inner: F,
557 kind: AddrKind,
558 ) -> Result<T, AddrParseError>
559 where
560 F: FnOnce(&mut Parser<'_>) -> Option<T>,
561 {
562 let result = inner(self);
563 if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind))
564 }
565
566 fn peek_char(&self) -> Option<char> {
568 self.state.first().map(|&b| char::from(b))
569 }
570
571 fn read_char(&mut self) -> Option<char> {
573 self.state.split_first().map(|(&b, tail)| {
574 self.state = tail;
575 char::from(b)
576 })
577 }
578
579 #[must_use]
580 fn read_given_char(&mut self, target: char) -> Option<()> {
582 self.read_atomically(|p| {
583 p.read_char()
584 .and_then(|c| if c == target { Some(()) } else { None })
585 })
586 }
587
588 fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T>
593 where
594 F: FnOnce(&mut Parser<'_>) -> Option<T>,
595 {
596 self.read_atomically(move |p| {
597 if index > 0 {
598 p.read_given_char(sep)?;
599 }
600 inner(p)
601 })
602 }
603
604 fn read_number<T: ReadNumberHelper>(
608 &mut self,
609 radix: u32,
610 max_digits: Option<usize>,
611 allow_zero_prefix: bool,
612 ) -> Option<T> {
613 self.read_atomically(move |p| {
614 let mut result = T::ZERO;
615 let mut digit_count = 0;
616 let has_leading_zero = p.peek_char() == Some('0');
617
618 while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
619 result = result.checked_mul(radix)?;
620 result = result.checked_add(digit)?;
621 digit_count += 1;
622 if let Some(max_digits) = max_digits {
623 if digit_count > max_digits {
624 return None;
625 }
626 }
627 }
628
629 if digit_count == 0 || (!allow_zero_prefix && has_leading_zero && digit_count > 1) {
630 None
631 } else {
632 Some(result)
633 }
634 })
635 }
636
637 pub(super) fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
639 self.read_atomically(|p| {
640 let mut groups = [0; 4];
641
642 for (i, slot) in groups.iter_mut().enumerate() {
643 *slot = p.read_separator('.', i, |p| {
644 p.read_number(10, Some(3), false)
647 })?;
648 }
649
650 Some(Ipv4Addr(groups))
651 })
652 }
653
654 pub(super) fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
656 fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
662 let limit = groups.len();
663
664 for (i, slot) in groups.iter_mut().enumerate() {
665 if i < limit - 1 {
668 let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());
669
670 if let Some(v4_addr) = ipv4 {
671 let [one, two, three, four] = v4_addr.0;
672 groups[i] = u16::from_be_bytes([one, two]);
673 groups[i + 1] = u16::from_be_bytes([three, four]);
674 return (i + 2, true);
675 }
676 }
677
678 let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));
679
680 match group {
681 Some(g) => *slot = g,
682 None => return (i, false),
683 }
684 }
685 (groups.len(), false)
686 }
687
688 self.read_atomically(|p| {
689 let mut head = [0; 8];
692 let (head_size, head_ipv4) = read_groups(p, &mut head);
693
694 if head_size == 8 {
695 return Some(head.into());
696 }
697
698 if head_ipv4 {
700 return None;
701 }
702
703 p.read_given_char(':')?;
706 p.read_given_char(':')?;
707
708 let mut tail = [0; 7];
711 let limit = 8 - (head_size + 1);
712 let (tail_size, _) = read_groups(p, &mut tail[..limit]);
713
714 head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]);
716
717 Some(head.into())
718 })
719 }
720 }
721
722 trait ReadNumberHelper: Sized {
723 const ZERO: Self;
724 fn checked_mul(&self, other: u32) -> Option<Self>;
725 fn checked_add(&self, other: u32) -> Option<Self>;
726 }
727
728 macro_rules! impl_helper {
729 ($($t:ty)*) => ($(impl ReadNumberHelper for $t {
730 const ZERO: Self = 0;
731 #[inline]
732 fn checked_mul(&self, other: u32) -> Option<Self> {
733 Self::checked_mul(*self, other.try_into().ok()?)
734 }
735 #[inline]
736 fn checked_add(&self, other: u32) -> Option<Self> {
737 Self::checked_add(*self, other.try_into().ok()?)
738 }
739 })*)
740 }
741
742 impl_helper! { u8 u16 u32 }
743
744 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
745 pub(super) enum AddrKind {
746 Ipv4,
747 Ipv6,
748 }
749}
750
751use parser::{AddrKind, Parser};
752
753#[derive(Debug, Clone, Copy, Eq, PartialEq)]
755pub struct AddrParseError(AddrKind);
756
757impl core::fmt::Display for AddrParseError {
758 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
759 f.write_str(match self.0 {
760 AddrKind::Ipv4 => "invalid IPv4 address syntax",
761 AddrKind::Ipv6 => "invalid IPv6 address syntax",
762 })
763 }
764}
765
766#[cfg(feature = "std")]
767impl ::std::error::Error for AddrParseError {}
768
769#[cfg(test)]
770mod tests {
771 use super::*;
772 #[cfg(feature = "alloc")]
773 use alloc::format;
774
775 #[cfg(feature = "alloc")]
776 static TESTS: &[(&str, bool)] = &[
777 ("", false),
778 ("localhost", true),
779 ("LOCALHOST", true),
780 (".localhost", false),
781 ("..localhost", false),
782 ("1.2.3.4", false),
783 ("127.0.0.1", false),
784 ("absolute.", true),
785 ("absolute..", false),
786 ("multiple.labels.absolute.", true),
787 ("foo.bar.com", true),
788 ("infix-hyphen-allowed.com", true),
789 ("-prefixhypheninvalid.com", false),
790 ("suffixhypheninvalid--", false),
791 ("suffixhypheninvalid-.com", false),
792 ("foo.lastlabelendswithhyphen-", false),
793 ("infix_underscore_allowed.com", true),
794 ("_prefixunderscorevalid.com", true),
795 ("labelendswithnumber1.bar.com", true),
796 ("xn--bcher-kva.example", true),
797 (
798 "sixtythreesixtythreesixtythreesixtythreesixtythreesixtythreesix.com",
799 true,
800 ),
801 (
802 "sixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfours.com",
803 false,
804 ),
805 (
806 "012345678901234567890123456789012345678901234567890123456789012.com",
807 true,
808 ),
809 (
810 "0123456789012345678901234567890123456789012345678901234567890123.com",
811 false,
812 ),
813 (
814 "01234567890123456789012345678901234567890123456789012345678901-.com",
815 false,
816 ),
817 (
818 "012345678901234567890123456789012345678901234567890123456789012-.com",
819 false,
820 ),
821 ("numeric-only-final-label.1", false),
822 ("numeric-only-final-label.absolute.1.", false),
823 ("1starts-with-number.com", true),
824 ("1Starts-with-number.com", true),
825 ("1.2.3.4.com", true),
826 ("123.numeric-only-first-label", true),
827 ("a123b.com", true),
828 ("numeric-only-middle-label.4.com", true),
829 ("1000-sans.badssl.com", true),
830 ("twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfi", true),
831 ("twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourc", false),
832 ];
833
834 #[cfg(feature = "alloc")]
835 #[test]
836 fn test_validation() {
837 for (input, expected) in TESTS {
838 #[cfg(feature = "std")]
839 println!("test: {:?} expected valid? {:?}", input, expected);
840 let name_ref = DnsName::try_from(*input);
841 assert_eq!(*expected, name_ref.is_ok());
842 let name = DnsName::try_from(input.to_string());
843 assert_eq!(*expected, name.is_ok());
844 }
845 }
846
847 #[cfg(feature = "alloc")]
848 #[test]
849 fn error_is_debug() {
850 assert_eq!(format!("{:?}", InvalidDnsNameError), "InvalidDnsNameError");
851 }
852
853 #[cfg(feature = "alloc")]
854 #[test]
855 fn error_is_display() {
856 assert_eq!(format!("{}", InvalidDnsNameError), "invalid dns name");
857 }
858
859 #[cfg(feature = "alloc")]
860 #[test]
861 fn dns_name_is_debug() {
862 let example = DnsName::try_from("example.com".to_string()).unwrap();
863 assert_eq!(format!("{:?}", example), "DnsName(\"example.com\")");
864 }
865
866 #[cfg(feature = "alloc")]
867 #[test]
868 fn dns_name_traits() {
869 let example = DnsName::try_from("example.com".to_string()).unwrap();
870 assert_eq!(example, example); #[cfg(feature = "std")]
873 {
874 use std::collections::HashSet;
875 let mut h = HashSet::<DnsName>::new();
876 h.insert(example);
877 }
878 }
879
880 #[cfg(feature = "alloc")]
881 #[test]
882 fn try_from_ascii_rejects_bad_utf8() {
883 assert_eq!(
884 format!("{:?}", DnsName::try_from(&b"\x80"[..])),
885 "Err(InvalidDnsNameError)"
886 );
887 }
888
889 const fn ipv4_address(
890 ip_address: &str,
891 octets: [u8; 4],
892 ) -> (&str, Result<Ipv4Addr, AddrParseError>) {
893 (ip_address, Ok(Ipv4Addr(octets)))
894 }
895
896 const IPV4_ADDRESSES: &[(&str, Result<Ipv4Addr, AddrParseError>)] = &[
897 ipv4_address("0.0.0.0", [0, 0, 0, 0]),
899 ipv4_address("1.1.1.1", [1, 1, 1, 1]),
900 ipv4_address("205.0.0.0", [205, 0, 0, 0]),
901 ipv4_address("0.205.0.0", [0, 205, 0, 0]),
902 ipv4_address("0.0.205.0", [0, 0, 205, 0]),
903 ipv4_address("0.0.0.205", [0, 0, 0, 205]),
904 ipv4_address("0.0.0.20", [0, 0, 0, 20]),
905 ("", Err(AddrParseError(AddrKind::Ipv4))),
907 ("...", Err(AddrParseError(AddrKind::Ipv4))),
908 (".0.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
909 ("0.0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
910 ("0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
911 ("0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
912 ("256.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
913 ("0.256.0.0", Err(AddrParseError(AddrKind::Ipv4))),
914 ("0.0.256.0", Err(AddrParseError(AddrKind::Ipv4))),
915 ("0.0.0.256", Err(AddrParseError(AddrKind::Ipv4))),
916 ("1..1.1.1", Err(AddrParseError(AddrKind::Ipv4))),
917 ("1.1..1.1", Err(AddrParseError(AddrKind::Ipv4))),
918 ("1.1.1..1", Err(AddrParseError(AddrKind::Ipv4))),
919 ("025.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
920 ("0.025.0.0", Err(AddrParseError(AddrKind::Ipv4))),
921 ("0.0.025.0", Err(AddrParseError(AddrKind::Ipv4))),
922 ("0.0.0.025", Err(AddrParseError(AddrKind::Ipv4))),
923 ("1234.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
924 ("0.1234.0.0", Err(AddrParseError(AddrKind::Ipv4))),
925 ("0.0.1234.0", Err(AddrParseError(AddrKind::Ipv4))),
926 ("0.0.0.1234", Err(AddrParseError(AddrKind::Ipv4))),
927 ];
928
929 #[test]
930 fn parse_ipv4_address_test() {
931 for &(ip_address, expected_result) in IPV4_ADDRESSES {
932 assert_eq!(Ipv4Addr::try_from(ip_address), expected_result);
933 }
934 }
935
936 const fn ipv6_address(
937 ip_address: &str,
938 octets: [u8; 16],
939 ) -> (&str, Result<Ipv6Addr, AddrParseError>) {
940 (ip_address, Ok(Ipv6Addr(octets)))
941 }
942
943 const IPV6_ADDRESSES: &[(&str, Result<Ipv6Addr, AddrParseError>)] = &[
944 ipv6_address(
946 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed",
947 [
948 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
949 0x3a, 0xed,
950 ],
951 ),
952 ipv6_address(
953 "2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED",
954 [
955 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
956 0x3a, 0xed,
957 ],
958 ),
959 ipv6_address(
960 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
961 [
962 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
963 0xff, 0xff,
964 ],
965 ),
966 ipv6_address(
967 "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
968 [
969 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
970 0xff, 0xff,
971 ],
972 ),
973 ipv6_address(
974 "FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
975 [
976 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
977 0xff, 0xff,
978 ],
979 ),
980 (
982 "ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
983 Err(AddrParseError(AddrKind::Ipv6)),
984 ),
985 (
986 "ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff",
987 Err(AddrParseError(AddrKind::Ipv6)),
988 ),
989 (
990 "ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff",
991 Err(AddrParseError(AddrKind::Ipv6)),
992 ),
993 (
994 "ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff",
995 Err(AddrParseError(AddrKind::Ipv6)),
996 ),
997 (
998 "ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff",
999 Err(AddrParseError(AddrKind::Ipv6)),
1000 ),
1001 (
1002 "ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff",
1003 Err(AddrParseError(AddrKind::Ipv6)),
1004 ),
1005 (
1006 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff",
1007 Err(AddrParseError(AddrKind::Ipv6)),
1008 ),
1009 (
1010 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg",
1011 Err(AddrParseError(AddrKind::Ipv6)),
1012 ),
1013 (
1015 ":ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1016 Err(AddrParseError(AddrKind::Ipv6)),
1017 ),
1018 (
1019 "ffff::ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1020 Err(AddrParseError(AddrKind::Ipv6)),
1021 ),
1022 (
1023 "ffff:ffff::ffff:ffff:ffff:ffff:ffff:ffff",
1024 Err(AddrParseError(AddrKind::Ipv6)),
1025 ),
1026 (
1027 "ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff",
1028 Err(AddrParseError(AddrKind::Ipv6)),
1029 ),
1030 (
1031 "ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff",
1032 Err(AddrParseError(AddrKind::Ipv6)),
1033 ),
1034 (
1035 "ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff",
1036 Err(AddrParseError(AddrKind::Ipv6)),
1037 ),
1038 (
1039 "ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff",
1040 Err(AddrParseError(AddrKind::Ipv6)),
1041 ),
1042 (
1043 "ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff",
1044 Err(AddrParseError(AddrKind::Ipv6)),
1045 ),
1046 (
1048 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:",
1049 Err(AddrParseError(AddrKind::Ipv6)),
1050 ),
1051 (
1052 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1053 Err(AddrParseError(AddrKind::Ipv6)),
1054 ),
1055 (
1057 "ga05:d018:076c:b685:e8ab:afd3:af51:3aed",
1058 Err(AddrParseError(AddrKind::Ipv6)),
1059 ),
1060 (
1062 ":a05:d018:076c:b685:e8ab:afd3:af51:3aed",
1063 Err(AddrParseError(AddrKind::Ipv6)),
1064 ),
1065 (
1067 "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:",
1068 Err(AddrParseError(AddrKind::Ipv6)),
1069 ),
1070 (
1072 "2a05:d018:076c:b685:e8ab:afd3:af51:3a::",
1073 Err(AddrParseError(AddrKind::Ipv6)),
1074 ),
1075 (
1077 "2a05::018:076c:b685:e8ab:afd3:af51:3aed",
1078 Err(AddrParseError(AddrKind::Ipv6)),
1079 ),
1080 (
1082 "2a056:d018:076c:b685:e8ab:afd3:af51:3ae",
1083 Err(AddrParseError(AddrKind::Ipv6)),
1084 ),
1085 (
1087 "2a0:d018:076c:b685:e8ab:afd3:af51:3aed ",
1088 Err(AddrParseError(AddrKind::Ipv6)),
1089 ),
1090 (
1092 "d018:076c:b685:e8ab:afd3:af51:3aed",
1093 Err(AddrParseError(AddrKind::Ipv6)),
1094 ),
1095 (
1097 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed",
1098 Err(AddrParseError(AddrKind::Ipv6)),
1099 ),
1100 ];
1101
1102 #[test]
1103 fn parse_ipv6_address_test() {
1104 for &(ip_address, expected_result) in IPV6_ADDRESSES {
1105 assert_eq!(Ipv6Addr::try_from(ip_address), expected_result);
1106 }
1107 }
1108
1109 #[test]
1110 fn try_from_ascii_ip_address_test() {
1111 const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[
1112 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1114 (
1116 "127.0.0.",
1118 Err(AddrParseError(AddrKind::Ipv6)),
1119 ),
1120 (
1122 "0000:0000:0000:0000:0000:0000:0000:0001",
1123 Ok(IpAddr::V6(Ipv6Addr([
1124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1125 ]))),
1126 ),
1127 (
1129 "example.com",
1131 Err(AddrParseError(AddrKind::Ipv6)),
1132 ),
1133 ];
1134 for &(ip_address, expected_result) in IP_ADDRESSES {
1135 assert_eq!(IpAddr::try_from(ip_address), expected_result)
1136 }
1137 }
1138
1139 #[test]
1140 fn try_from_ascii_str_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 #[cfg(feature = "std")]
1171 fn to_str() {
1172 let domain_str = "example.com";
1173 let domain_servername = ServerName::try_from(domain_str).unwrap();
1174 assert_eq!(domain_str, domain_servername.to_str());
1175
1176 let ipv4_str = "127.0.0.1";
1177 let ipv4_servername = ServerName::try_from("127.0.0.1").unwrap();
1178 assert_eq!(ipv4_str, ipv4_servername.to_str());
1179
1180 let ipv6_str = "::1";
1181 let ipv6_servername = ServerName::try_from(ipv6_str).unwrap();
1182 assert_eq!("::1", ipv6_servername.to_str());
1183 }
1184}