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