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 (
841 "twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfi",
842 true,
843 ),
844 (
845 "twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourc",
846 false,
847 ),
848 ];
849
850 #[cfg(feature = "alloc")]
851 #[test]
852 fn test_validation() {
853 for (input, expected) in TESTS {
854 #[cfg(feature = "std")]
855 println!("test: {:?} expected valid? {:?}", input, expected);
856 let name_ref = DnsName::try_from(*input);
857 assert_eq!(*expected, name_ref.is_ok());
858 let name = DnsName::try_from(input.to_string());
859 assert_eq!(*expected, name.is_ok());
860 }
861 }
862
863 #[cfg(feature = "alloc")]
864 #[test]
865 fn error_is_debug() {
866 assert_eq!(format!("{:?}", InvalidDnsNameError), "InvalidDnsNameError");
867 }
868
869 #[cfg(feature = "alloc")]
870 #[test]
871 fn error_is_display() {
872 assert_eq!(format!("{}", InvalidDnsNameError), "invalid dns name");
873 }
874
875 #[cfg(feature = "alloc")]
876 #[test]
877 fn dns_name_is_debug() {
878 let example = DnsName::try_from("example.com".to_string()).unwrap();
879 assert_eq!(format!("{:?}", example), "DnsName(\"example.com\")");
880 }
881
882 #[cfg(feature = "alloc")]
883 #[test]
884 fn dns_name_traits() {
885 let example = DnsName::try_from("example.com".to_string()).unwrap();
886 assert_eq!(example, example); #[cfg(feature = "std")]
889 {
890 use std::collections::HashSet;
891 let mut h = HashSet::<DnsName>::new();
892 h.insert(example);
893 }
894 }
895
896 #[cfg(feature = "alloc")]
897 #[test]
898 fn try_from_ascii_rejects_bad_utf8() {
899 assert_eq!(
900 format!("{:?}", DnsName::try_from(&b"\x80"[..])),
901 "Err(InvalidDnsNameError)"
902 );
903 }
904
905 const fn ipv4_address(
906 ip_address: &str,
907 octets: [u8; 4],
908 ) -> (&str, Result<Ipv4Addr, AddrParseError>) {
909 (ip_address, Ok(Ipv4Addr(octets)))
910 }
911
912 const IPV4_ADDRESSES: &[(&str, Result<Ipv4Addr, AddrParseError>)] = &[
913 ipv4_address("0.0.0.0", [0, 0, 0, 0]),
915 ipv4_address("1.1.1.1", [1, 1, 1, 1]),
916 ipv4_address("205.0.0.0", [205, 0, 0, 0]),
917 ipv4_address("0.205.0.0", [0, 205, 0, 0]),
918 ipv4_address("0.0.205.0", [0, 0, 205, 0]),
919 ipv4_address("0.0.0.205", [0, 0, 0, 205]),
920 ipv4_address("0.0.0.20", [0, 0, 0, 20]),
921 ("", Err(AddrParseError(AddrKind::Ipv4))),
923 ("...", Err(AddrParseError(AddrKind::Ipv4))),
924 (".0.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
925 ("0.0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
926 ("0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
927 ("0.0.0.", Err(AddrParseError(AddrKind::Ipv4))),
928 ("256.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
929 ("0.256.0.0", Err(AddrParseError(AddrKind::Ipv4))),
930 ("0.0.256.0", Err(AddrParseError(AddrKind::Ipv4))),
931 ("0.0.0.256", Err(AddrParseError(AddrKind::Ipv4))),
932 ("1..1.1.1", Err(AddrParseError(AddrKind::Ipv4))),
933 ("1.1..1.1", Err(AddrParseError(AddrKind::Ipv4))),
934 ("1.1.1..1", Err(AddrParseError(AddrKind::Ipv4))),
935 ("025.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
936 ("0.025.0.0", Err(AddrParseError(AddrKind::Ipv4))),
937 ("0.0.025.0", Err(AddrParseError(AddrKind::Ipv4))),
938 ("0.0.0.025", Err(AddrParseError(AddrKind::Ipv4))),
939 ("1234.0.0.0", Err(AddrParseError(AddrKind::Ipv4))),
940 ("0.1234.0.0", Err(AddrParseError(AddrKind::Ipv4))),
941 ("0.0.1234.0", Err(AddrParseError(AddrKind::Ipv4))),
942 ("0.0.0.1234", Err(AddrParseError(AddrKind::Ipv4))),
943 ];
944
945 #[test]
946 fn parse_ipv4_address_test() {
947 for &(ip_address, expected_result) in IPV4_ADDRESSES {
948 assert_eq!(Ipv4Addr::try_from(ip_address), expected_result);
949 }
950 }
951
952 const fn ipv6_address(
953 ip_address: &str,
954 octets: [u8; 16],
955 ) -> (&str, Result<Ipv6Addr, AddrParseError>) {
956 (ip_address, Ok(Ipv6Addr(octets)))
957 }
958
959 const IPV6_ADDRESSES: &[(&str, Result<Ipv6Addr, AddrParseError>)] = &[
960 ipv6_address(
962 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed",
963 [
964 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
965 0x3a, 0xed,
966 ],
967 ),
968 ipv6_address(
969 "2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED",
970 [
971 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
972 0x3a, 0xed,
973 ],
974 ),
975 ipv6_address(
976 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
977 [
978 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
979 0xff, 0xff,
980 ],
981 ),
982 ipv6_address(
983 "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
984 [
985 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
986 0xff, 0xff,
987 ],
988 ),
989 ipv6_address(
990 "FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
991 [
992 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
993 0xff, 0xff,
994 ],
995 ),
996 (
998 "ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
999 Err(AddrParseError(AddrKind::Ipv6)),
1000 ),
1001 (
1002 "ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff",
1003 Err(AddrParseError(AddrKind::Ipv6)),
1004 ),
1005 (
1006 "ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff",
1007 Err(AddrParseError(AddrKind::Ipv6)),
1008 ),
1009 (
1010 "ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff",
1011 Err(AddrParseError(AddrKind::Ipv6)),
1012 ),
1013 (
1014 "ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff",
1015 Err(AddrParseError(AddrKind::Ipv6)),
1016 ),
1017 (
1018 "ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff",
1019 Err(AddrParseError(AddrKind::Ipv6)),
1020 ),
1021 (
1022 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff",
1023 Err(AddrParseError(AddrKind::Ipv6)),
1024 ),
1025 (
1026 "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg",
1027 Err(AddrParseError(AddrKind::Ipv6)),
1028 ),
1029 (
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 (
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 (
1064 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:",
1065 Err(AddrParseError(AddrKind::Ipv6)),
1066 ),
1067 (
1068 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1069 Err(AddrParseError(AddrKind::Ipv6)),
1070 ),
1071 (
1073 "ga05:d018:076c:b685:e8ab:afd3:af51:3aed",
1074 Err(AddrParseError(AddrKind::Ipv6)),
1075 ),
1076 (
1078 ":a05:d018:076c:b685:e8ab:afd3:af51:3aed",
1079 Err(AddrParseError(AddrKind::Ipv6)),
1080 ),
1081 (
1083 "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:",
1084 Err(AddrParseError(AddrKind::Ipv6)),
1085 ),
1086 (
1088 "2a05:d018:076c:b685:e8ab:afd3:af51:3a::",
1089 Err(AddrParseError(AddrKind::Ipv6)),
1090 ),
1091 (
1093 "2a05::018:076c:b685:e8ab:afd3:af51:3aed",
1094 Err(AddrParseError(AddrKind::Ipv6)),
1095 ),
1096 (
1098 "2a056:d018:076c:b685:e8ab:afd3:af51:3ae",
1099 Err(AddrParseError(AddrKind::Ipv6)),
1100 ),
1101 (
1103 "2a0:d018:076c:b685:e8ab:afd3:af51:3aed ",
1104 Err(AddrParseError(AddrKind::Ipv6)),
1105 ),
1106 (
1108 "d018:076c:b685:e8ab:afd3:af51:3aed",
1109 Err(AddrParseError(AddrKind::Ipv6)),
1110 ),
1111 (
1113 "2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed",
1114 Err(AddrParseError(AddrKind::Ipv6)),
1115 ),
1116 ];
1117
1118 #[test]
1119 fn parse_ipv6_address_test() {
1120 for &(ip_address, expected_result) in IPV6_ADDRESSES {
1121 assert_eq!(Ipv6Addr::try_from(ip_address), expected_result);
1122 }
1123 }
1124
1125 #[test]
1126 fn try_from_ascii_ip_address_test() {
1127 const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[
1128 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1130 (
1132 "127.0.0.",
1134 Err(AddrParseError(AddrKind::Ipv6)),
1135 ),
1136 (
1138 "0000:0000:0000:0000:0000:0000:0000:0001",
1139 Ok(IpAddr::V6(Ipv6Addr([
1140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1141 ]))),
1142 ),
1143 (
1145 "example.com",
1147 Err(AddrParseError(AddrKind::Ipv6)),
1148 ),
1149 ];
1150 for &(ip_address, expected_result) in IP_ADDRESSES {
1151 assert_eq!(IpAddr::try_from(ip_address), expected_result)
1152 }
1153 }
1154
1155 #[test]
1156 fn try_from_ascii_str_ip_address_test() {
1157 const IP_ADDRESSES: &[(&str, Result<IpAddr, AddrParseError>)] = &[
1158 ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))),
1160 (
1162 "127.0.0.",
1164 Err(AddrParseError(AddrKind::Ipv6)),
1165 ),
1166 (
1168 "0000:0000:0000:0000:0000:0000:0000:0001",
1169 Ok(IpAddr::V6(Ipv6Addr([
1170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1171 ]))),
1172 ),
1173 (
1175 "example.com",
1177 Err(AddrParseError(AddrKind::Ipv6)),
1178 ),
1179 ];
1180 for &(ip_address, expected_result) in IP_ADDRESSES {
1181 assert_eq!(IpAddr::try_from(ip_address), expected_result)
1182 }
1183 }
1184
1185 #[test]
1186 #[cfg(feature = "std")]
1187 fn to_str() {
1188 let domain_str = "example.com";
1189 let domain_servername = ServerName::try_from(domain_str).unwrap();
1190 assert_eq!(domain_str, domain_servername.to_str());
1191
1192 let ipv4_str = "127.0.0.1";
1193 let ipv4_servername = ServerName::try_from("127.0.0.1").unwrap();
1194 assert_eq!(ipv4_str, ipv4_servername.to_str());
1195
1196 let ipv6_str = "::1";
1197 let ipv6_servername = ServerName::try_from(ipv6_str).unwrap();
1198 assert_eq!("::1", ipv6_servername.to_str());
1199 }
1200}