1use core::fmt;
36use core::str::FromStr;
37
38#[cfg(feature = "std")]
39use super::IpAddr;
40use super::{Host, Port};
41
42#[cfg(feature = "serde")]
43use serde::{Deserialize, Serialize};
44
45#[cfg(feature = "arbitrary")]
46use arbitrary::Arbitrary;
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum SocketAddrError {
51 MissingPortSeparator,
53 InvalidHost,
55 InvalidPort,
57 EmptyInput,
59}
60
61impl fmt::Display for SocketAddrError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 Self::MissingPortSeparator => write!(f, "missing port separator ':'"),
65 Self::InvalidHost => write!(f, "invalid host"),
66 Self::InvalidPort => write!(f, "invalid port"),
67 Self::EmptyInput => write!(f, "empty input"),
68 }
69 }
70}
71
72#[cfg(feature = "std")]
73impl std::error::Error for SocketAddrError {}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
77pub enum StdConversionError {
78 NotIpAddress,
80 PortZero,
82}
83
84impl fmt::Display for StdConversionError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Self::NotIpAddress => write!(
88 f,
89 "cannot convert to std::net::SocketAddr: host is not an IP address"
90 ),
91 Self::PortZero => write!(
92 f,
93 "cannot convert from std::net::SocketAddr: port 0 is not allowed"
94 ),
95 }
96 }
97}
98
99#[cfg(feature = "std")]
100impl std::error::Error for StdConversionError {}
101
102#[derive(Debug, Clone, PartialEq, Eq, Hash)]
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136pub struct SocketAddr {
137 host: Host,
139 port: Port,
141}
142
143#[cfg(feature = "arbitrary")]
144impl<'a> Arbitrary<'a> for SocketAddr {
145 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
146 Ok(Self {
147 host: Host::arbitrary(u)?,
148 port: Port::arbitrary(u)?,
149 })
150 }
151}
152
153impl SocketAddr {
154 #[must_use]
170 #[inline]
171 pub const fn new(host: Host, port: Port) -> Self {
172 Self { host, port }
173 }
174
175 #[must_use]
190 #[inline]
191 pub const fn as_host(&self) -> &Host {
192 &self.host
193 }
194
195 #[must_use]
210 #[inline]
211 pub const fn as_port(&self) -> &Port {
212 &self.port
213 }
214
215 #[inline]
231 pub fn set_host(&mut self, host: Host) {
232 self.host = host;
233 }
234
235 #[inline]
251 pub const fn set_port(&mut self, port: Port) {
252 self.port = port;
253 }
254
255 #[must_use]
275 #[inline]
276 pub fn into_parts(self) -> (Host, Port) {
277 (self.host, self.port)
278 }
279
280 pub fn parse_str(s: &str) -> Result<Self, SocketAddrError> {
312 if s.is_empty() {
313 return Err(SocketAddrError::EmptyInput);
314 }
315
316 let Some(colon_pos) = s.rfind(':') else {
318 return Err(SocketAddrError::MissingPortSeparator);
319 };
320
321 let host_str = &s[..colon_pos];
323 let port_str = &s[colon_pos + 1..];
324
325 if host_str.is_empty() || port_str.is_empty() {
327 return Err(SocketAddrError::MissingPortSeparator);
328 }
329
330 let host_str = if host_str.starts_with('[') && host_str.ends_with(']') {
332 &host_str[1..host_str.len() - 1]
333 } else {
334 host_str
335 };
336
337 let host = Host::parse_str(host_str).map_err(|_| SocketAddrError::InvalidHost)?;
339
340 let port = port_str
342 .parse::<Port>()
343 .map_err(|_| SocketAddrError::InvalidPort)?;
344
345 Ok(Self { host, port })
346 }
347
348 #[must_use]
363 #[inline]
364 pub const fn is_ip(&self) -> bool {
365 self.host.is_ipaddr()
366 }
367
368 #[must_use]
383 #[inline]
384 pub const fn is_domain_name(&self) -> bool {
385 self.host.is_domainname()
386 }
387
388 #[must_use]
406 #[inline]
407 pub const fn is_hostname(&self) -> bool {
408 self.host.is_hostname()
409 }
410}
411
412impl FromStr for SocketAddr {
413 type Err = SocketAddrError;
414
415 fn from_str(s: &str) -> Result<Self, Self::Err> {
416 Self::parse_str(s)
417 }
418}
419
420impl fmt::Display for SocketAddr {
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 match &self.host {
424 Host::IpAddr(ip) if ip.as_inner().is_ipv6() => {
425 write!(f, "[{}]:{}", self.host, self.port)
426 }
427 _ => write!(f, "{}:{}", self.host, self.port),
428 }
429 }
430}
431
432#[cfg(feature = "std")]
433impl TryFrom<std::net::SocketAddr> for SocketAddr {
434 type Error = StdConversionError;
435
436 fn try_from(addr: std::net::SocketAddr) -> Result<Self, Self::Error> {
437 let port = Port::new(addr.port()).map_err(|_| StdConversionError::PortZero)?;
438 Ok(Self {
439 host: Host::from_ipaddr(IpAddr::from(addr.ip())),
440 port,
441 })
442 }
443}
444
445#[cfg(feature = "std")]
446impl TryFrom<SocketAddr> for std::net::SocketAddr {
447 type Error = StdConversionError;
448
449 fn try_from(addr: SocketAddr) -> Result<Self, Self::Error> {
450 match addr.host {
451 Host::IpAddr(ip) => Ok(Self::new(ip.into(), addr.port.as_u16())),
452 Host::DomainName(_) | Host::Hostname(_) => Err(StdConversionError::NotIpAddress),
453 }
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 #[test]
462 fn test_new() {
463 let host = Host::parse_str("192.168.1.1").unwrap();
464 let port = Port::new(8080).unwrap();
465 let addr = SocketAddr::new(host, port);
466 assert!(addr.as_host().is_ipaddr());
467 assert_eq!(addr.as_port().as_u16(), 8080);
468 }
469
470 #[test]
471 fn test_parse_ipv4() {
472 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
473 assert!(addr.as_host().is_ipaddr());
474 assert_eq!(addr.as_port().as_u16(), 8080);
475 assert_eq!(format!("{addr}"), "192.168.1.1:8080");
476 }
477
478 #[test]
479 fn test_parse_ipv6() {
480 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
481 assert!(addr.as_host().is_ipaddr());
482 assert_eq!(addr.as_port().as_u16(), 8080);
483 }
484
485 #[test]
486 fn test_parse_domainname() {
487 let addr: SocketAddr = "example.com:443".parse().unwrap();
488 assert!(addr.as_host().is_domainname());
489 assert_eq!(addr.as_port().as_u16(), 443);
490 assert_eq!(format!("{addr}"), "example.com:443");
491 }
492
493 #[test]
494 fn test_parse_domainname_with_digits() {
495 let addr: SocketAddr = "123.example.com:443".parse().unwrap();
496 assert!(addr.as_host().is_domainname());
497 assert_eq!(addr.as_port().as_u16(), 443);
498 }
499
500 #[test]
501 fn test_parse_str_empty() {
502 assert!(SocketAddr::parse_str("").is_err());
503 }
504
505 #[test]
506 fn test_parse_str_missing_separator() {
507 assert!(SocketAddr::parse_str("192.168.1.1").is_err());
508 }
509
510 #[test]
511 fn test_parse_str_empty_host() {
512 assert!(SocketAddr::parse_str(":8080").is_err());
513 }
514
515 #[test]
516 fn test_parse_str_empty_port() {
517 assert!(SocketAddr::parse_str("192.168.1.1:").is_err());
518 }
519
520 #[test]
521 fn test_parse_str_invalid_host() {
522 assert!(SocketAddr::parse_str("-invalid:8080").is_err());
523 }
524
525 #[test]
526 fn test_parse_str_invalid_port() {
527 assert!(SocketAddr::parse_str("192.168.1.1:invalid").is_err());
528 }
529
530 #[test]
531 fn test_parse_str_port_zero() {
532 assert!(SocketAddr::parse_str("192.168.1.1:0").is_err());
533 }
534
535 #[test]
536 fn test_parse_str_port_out_of_range() {
537 assert!(SocketAddr::parse_str("192.168.1.1:99999").is_err());
538 }
539
540 #[test]
541 fn test_set_host() {
542 let mut addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
543 let new_host = Host::parse_str("10.0.0.1").unwrap();
544 addr.set_host(new_host);
545 assert_eq!(format!("{addr}"), "10.0.0.1:8080");
546 }
547
548 #[test]
549 fn test_set_port() {
550 let mut addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
551 let new_port = Port::new(9090).unwrap();
552 addr.set_port(new_port);
553 assert_eq!(format!("{addr}"), "192.168.1.1:9090");
554 }
555
556 #[test]
557 fn test_into_parts() {
558 let host = Host::parse_str("192.168.1.1").unwrap();
559 let port = Port::new(8080).unwrap();
560 let addr = SocketAddr::new(host, port);
561
562 let (h, p) = addr.into_parts();
563 assert!(h.is_ipaddr());
564 assert_eq!(p.as_u16(), 8080);
565 }
566
567 #[test]
568 fn test_as_host_and_as_port() {
569 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
570 assert!(addr.as_host().is_ipaddr());
571 assert_eq!(addr.as_port().as_u16(), 8080);
572 }
573
574 #[test]
575 fn test_is_ip() {
576 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
577 assert!(addr.is_ip());
578 assert!(!addr.is_domain_name());
579 assert!(!addr.is_hostname());
580 }
581
582 #[test]
583 fn test_is_domain_name() {
584 let addr: SocketAddr = "example.com:443".parse().unwrap();
585 assert!(!addr.is_ip());
586 assert!(addr.is_domain_name());
587 assert!(!addr.is_hostname());
588 }
589
590 #[test]
591 fn test_host_is_domainname() {
592 let addr: SocketAddr = "example.com:443".parse().unwrap();
593 assert!(addr.as_host().is_domainname());
594 }
595
596 #[test]
597 fn test_socket_addr_is_domain_name() {
598 let addr: SocketAddr = "example.com:443".parse().unwrap();
599 assert!(addr.is_domain_name());
600 }
601
602 #[test]
603 fn test_is_hostname() {
604 let hostname = crate::net::Hostname::new("localhost").unwrap();
605 let host = Host::from_hostname(hostname);
606 let port = Port::new(3000).unwrap();
607 let addr = SocketAddr::new(host, port);
608 assert!(!addr.is_ip());
609 assert!(!addr.is_domain_name());
610 assert!(addr.is_hostname());
611 }
612
613 #[test]
614 fn test_equality() {
615 let addr1: SocketAddr = "192.168.1.1:8080".parse().unwrap();
616 let addr2: SocketAddr = "192.168.1.1:8080".parse().unwrap();
617 let addr3: SocketAddr = "192.168.1.1:9090".parse().unwrap();
618 let addr4: SocketAddr = "10.0.0.1:8080".parse().unwrap();
619
620 assert_eq!(addr1, addr2);
621 assert_ne!(addr1, addr3);
622 assert_ne!(addr1, addr4);
623 }
624
625 #[test]
626 fn test_clone() {
627 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
628 let addr2 = addr.clone();
629 assert_eq!(addr, addr2);
630 }
631
632 #[test]
633 fn test_display() {
634 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
635 assert_eq!(format!("{addr}"), "192.168.1.1:8080");
636
637 let addr: SocketAddr = "example.com:443".parse().unwrap();
638 assert_eq!(format!("{addr}"), "example.com:443");
639
640 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
642 assert_eq!(format!("{addr}"), "[::1]:8080");
643
644 let addr: SocketAddr = "[2001:db8::1]:443".parse().unwrap();
645 assert_eq!(format!("{addr}"), "[2001:db8::1]:443");
646 }
647
648 #[test]
649 fn test_debug() {
650 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
651 let debug = format!("{:?}", addr);
652 assert!(debug.contains("SocketAddr"));
653 }
654
655 #[test]
656 fn test_hash() {
657 use core::hash::Hash;
658 use core::hash::Hasher;
659
660 #[derive(Default)]
661 struct SimpleHasher(u64);
662
663 impl Hasher for SimpleHasher {
664 fn finish(&self) -> u64 {
665 self.0
666 }
667
668 fn write(&mut self, bytes: &[u8]) {
669 for byte in bytes {
670 self.0 = self.0.wrapping_mul(31).wrapping_add(*byte as u64);
671 }
672 }
673 }
674
675 let addr1: SocketAddr = "192.168.1.1:8080".parse().unwrap();
676 let addr2: SocketAddr = "192.168.1.1:8080".parse().unwrap();
677 let addr3: SocketAddr = "10.0.0.1:8080".parse().unwrap();
678
679 let mut hasher1 = SimpleHasher::default();
680 let mut hasher2 = SimpleHasher::default();
681 let mut hasher3 = SimpleHasher::default();
682
683 addr1.hash(&mut hasher1);
684 addr2.hash(&mut hasher2);
685 addr3.hash(&mut hasher3);
686
687 assert_eq!(hasher1.finish(), hasher2.finish());
688 assert_ne!(hasher1.finish(), hasher3.finish());
689 }
690
691 #[test]
692 fn test_parse_ipv6_with_brackets() {
693 let addr: SocketAddr = "[2001:db8::1]:8080".parse().unwrap();
694 assert!(addr.as_host().is_ipaddr());
695 assert_eq!(addr.as_port().as_u16(), 8080);
696 }
697
698 #[test]
699 fn test_parse_localhost() {
700 let addr: SocketAddr = "localhost:3000".parse().unwrap();
701 assert!(addr.as_host().is_domainname());
702 assert_eq!(addr.as_port().as_u16(), 3000);
703 }
704
705 #[test]
706 fn test_parse_system_port() {
707 let addr: SocketAddr = "192.168.1.1:80".parse().unwrap();
708 assert_eq!(addr.as_port().as_u16(), 80);
709 assert!(addr.as_port().is_system_port());
710 }
711
712 #[test]
713 fn test_parse_registered_port() {
714 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
715 assert_eq!(addr.as_port().as_u16(), 8080);
716 assert!(addr.as_port().is_registered_port());
717 }
718
719 #[test]
720 fn test_parse_dynamic_port() {
721 let addr: SocketAddr = "192.168.1.1:50000".parse().unwrap();
722 assert_eq!(addr.as_port().as_u16(), 50000);
723 assert!(addr.as_port().is_dynamic_port());
724 }
725
726 #[test]
727 fn test_error_display() {
728 let err = SocketAddrError::MissingPortSeparator;
729 assert_eq!(format!("{err}"), "missing port separator ':'");
730
731 let err = SocketAddrError::InvalidHost;
732 assert_eq!(format!("{err}"), "invalid host");
733
734 let err = SocketAddrError::InvalidPort;
735 assert_eq!(format!("{err}"), "invalid port");
736
737 let err = SocketAddrError::EmptyInput;
738 assert_eq!(format!("{err}"), "empty input");
739 }
740
741 #[test]
742 fn test_parse_str_method() {
743 let addr = SocketAddr::parse_str("192.168.1.1:8080").unwrap();
744 assert!(addr.as_host().is_ipaddr());
745 assert_eq!(addr.as_port().as_u16(), 8080);
746 }
747
748 #[test]
749 fn test_case_insensitive() {
750 let addr1: SocketAddr = "EXAMPLE.COM:443".parse().unwrap();
751 let addr2: SocketAddr = "example.com:443".parse().unwrap();
752 assert_eq!(addr1, addr2);
753 }
754
755 #[test]
756 fn test_ipv4_loopback() {
757 let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
758 assert!(addr.as_host().is_ipaddr());
759 }
760
761 #[test]
762 fn test_ipv4_private() {
763 let addr: SocketAddr = "10.0.0.1:8080".parse().unwrap();
764 assert!(addr.as_host().is_ipaddr());
765 }
766
767 #[test]
768 fn test_ipv6_loopback() {
769 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
770 assert!(addr.as_host().is_ipaddr());
771 }
772
773 #[test]
774 fn test_numeric_domainname() {
775 let addr: SocketAddr = "123:8080".parse().unwrap();
776 assert!(addr.as_host().is_domainname());
777 }
778
779 #[test]
780 fn test_multi_label_domainname() {
781 let addr: SocketAddr = "api.v1.example.com:443".parse().unwrap();
782 assert!(addr.as_host().is_domainname());
783 }
784
785 #[test]
786 fn test_max_port() {
787 let addr: SocketAddr = "192.168.1.1:65535".parse().unwrap();
788 assert_eq!(addr.as_port().as_u16(), 65535);
789 }
790
791 #[test]
792 fn test_min_port() {
793 let addr: SocketAddr = "192.168.1.1:1".parse().unwrap();
794 assert_eq!(addr.as_port().as_u16(), 1);
795 }
796
797 #[cfg(feature = "std")]
798 #[test]
799 fn test_try_from_std_socket_addr() {
800 let std_addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
801 let addr = SocketAddr::try_from(std_addr).unwrap();
802 assert!(addr.as_host().is_ipaddr());
803 assert_eq!(addr.as_port().as_u16(), 8080);
804 }
805
806 #[cfg(feature = "std")]
807 #[test]
808 fn test_try_from_std_socket_addr_ipv6() {
809 let std_addr = std::net::SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 1], 8080));
810 let addr = SocketAddr::try_from(std_addr).unwrap();
811 assert!(addr.as_host().is_ipaddr());
812 assert_eq!(addr.as_port().as_u16(), 8080);
813 }
814
815 #[cfg(feature = "std")]
816 #[test]
817 fn test_try_from_std_socket_addr_port_zero_fails() {
818 let std_addr = std::net::SocketAddr::from(([127, 0, 0, 1], 0));
819 let result = SocketAddr::try_from(std_addr);
820 assert!(result.is_err());
821 assert_eq!(result.unwrap_err(), StdConversionError::PortZero);
822 }
823
824 #[cfg(feature = "std")]
825 #[test]
826 fn test_try_to_std_socket_addr_ip() {
827 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
828 let std_addr = std::net::SocketAddr::try_from(addr).unwrap();
829 assert_eq!(std_addr.ip(), std::net::IpAddr::from([192, 168, 1, 1]));
830 assert_eq!(std_addr.port(), 8080);
831 }
832
833 #[cfg(feature = "std")]
834 #[test]
835 fn test_try_to_std_socket_addr_domainname_fails() {
836 let addr: SocketAddr = "example.com:443".parse().unwrap();
837 let result = std::net::SocketAddr::try_from(addr);
838 assert!(result.is_err());
839 assert_eq!(result.unwrap_err(), StdConversionError::NotIpAddress);
840 }
841
842 #[cfg(feature = "std")]
843 #[test]
844 fn test_try_to_std_socket_addr_hostname_fails() {
845 let hostname = crate::net::Hostname::new("localhost").unwrap();
846 let host = Host::from_hostname(hostname);
847 let port = Port::new(3000).unwrap();
848 let addr = SocketAddr::new(host, port);
849 let result = std::net::SocketAddr::try_from(addr);
850 assert!(result.is_err());
851 assert_eq!(result.unwrap_err(), StdConversionError::NotIpAddress);
852 }
853
854 #[cfg(feature = "std")]
855 #[test]
856 fn test_std_conversion_error_display() {
857 let err = StdConversionError::NotIpAddress;
858 assert_eq!(
859 format!("{err}"),
860 "cannot convert to std::net::SocketAddr: host is not an IP address"
861 );
862
863 let err = StdConversionError::PortZero;
864 assert_eq!(
865 format!("{err}"),
866 "cannot convert from std::net::SocketAddr: port 0 is not allowed"
867 );
868 }
869
870 #[cfg(feature = "std")]
871 #[test]
872 fn test_roundtrip_std_socket_addr() {
873 let std_addr1 = std::net::SocketAddr::from(([192, 168, 1, 1], 8080));
874 let addr = SocketAddr::try_from(std_addr1).unwrap();
875 let std_addr2 = std::net::SocketAddr::try_from(addr).unwrap();
876 assert_eq!(std_addr1, std_addr2);
877 }
878}