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)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133pub struct SocketAddr {
134 host: Host,
136 port: Port,
138}
139
140#[cfg(feature = "arbitrary")]
141impl<'a> Arbitrary<'a> for SocketAddr {
142 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
143 Ok(Self {
144 host: Host::arbitrary(u)?,
145 port: Port::arbitrary(u)?,
146 })
147 }
148}
149
150impl SocketAddr {
151 #[must_use]
164 #[inline]
165 pub const fn new(host: Host, port: Port) -> Self {
166 Self { host, port }
167 }
168
169 #[must_use]
181 #[inline]
182 pub const fn as_host(&self) -> &Host {
183 &self.host
184 }
185
186 #[must_use]
198 #[inline]
199 pub const fn as_port(&self) -> &Port {
200 &self.port
201 }
202
203 #[inline]
216 pub fn set_host(&mut self, host: Host) {
217 self.host = host;
218 }
219
220 #[inline]
233 pub const fn set_port(&mut self, port: Port) {
234 self.port = port;
235 }
236
237 #[must_use]
254 #[inline]
255 pub fn into_parts(self) -> (Host, Port) {
256 (self.host, self.port)
257 }
258
259 pub fn parse_str(s: &str) -> Result<Self, SocketAddrError> {
288 if s.is_empty() {
289 return Err(SocketAddrError::EmptyInput);
290 }
291
292 let Some(colon_pos) = s.rfind(':') else {
294 return Err(SocketAddrError::MissingPortSeparator);
295 };
296
297 let host_str = &s[..colon_pos];
299 let port_str = &s[colon_pos + 1..];
300
301 if host_str.is_empty() || port_str.is_empty() {
303 return Err(SocketAddrError::MissingPortSeparator);
304 }
305
306 let host_str = if host_str.starts_with('[') && host_str.ends_with(']') {
308 &host_str[1..host_str.len() - 1]
309 } else {
310 host_str
311 };
312
313 let host = Host::parse_str(host_str).map_err(|_| SocketAddrError::InvalidHost)?;
315
316 let port = port_str
318 .parse::<Port>()
319 .map_err(|_| SocketAddrError::InvalidPort)?;
320
321 Ok(Self { host, port })
322 }
323
324 #[must_use]
336 #[inline]
337 pub const fn is_ip(&self) -> bool {
338 self.host.is_ipaddr()
339 }
340
341 #[must_use]
353 #[inline]
354 pub const fn is_domain_name(&self) -> bool {
355 self.host.is_domainname()
356 }
357
358 #[must_use]
373 #[inline]
374 pub const fn is_hostname(&self) -> bool {
375 self.host.is_hostname()
376 }
377}
378
379impl FromStr for SocketAddr {
380 type Err = SocketAddrError;
381
382 fn from_str(s: &str) -> Result<Self, Self::Err> {
383 Self::parse_str(s)
384 }
385}
386
387impl fmt::Display for SocketAddr {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 match &self.host {
391 Host::IpAddr(ip) if ip.as_inner().is_ipv6() => {
392 write!(f, "[{}]:{}", self.host, self.port)
393 }
394 _ => write!(f, "{}:{}", self.host, self.port),
395 }
396 }
397}
398
399#[cfg(feature = "std")]
400impl TryFrom<std::net::SocketAddr> for SocketAddr {
401 type Error = StdConversionError;
402
403 fn try_from(addr: std::net::SocketAddr) -> Result<Self, Self::Error> {
404 let port = Port::new(addr.port()).map_err(|_| StdConversionError::PortZero)?;
405 Ok(Self {
406 host: Host::from_ipaddr(IpAddr::from(addr.ip())),
407 port,
408 })
409 }
410}
411
412#[cfg(feature = "std")]
413impl TryFrom<SocketAddr> for std::net::SocketAddr {
414 type Error = StdConversionError;
415
416 fn try_from(addr: SocketAddr) -> Result<Self, Self::Error> {
417 match addr.host {
418 Host::IpAddr(ip) => Ok(Self::new(ip.into(), addr.port.as_u16())),
419 Host::DomainName(_) | Host::Hostname(_) => Err(StdConversionError::NotIpAddress),
420 }
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn test_new() {
430 let host = Host::parse_str("192.168.1.1").unwrap();
431 let port = Port::new(8080).unwrap();
432 let addr = SocketAddr::new(host, port);
433 assert!(addr.as_host().is_ipaddr());
434 assert_eq!(addr.as_port().as_u16(), 8080);
435 }
436
437 #[test]
438 fn test_parse_ipv4() {
439 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
440 assert!(addr.as_host().is_ipaddr());
441 assert_eq!(addr.as_port().as_u16(), 8080);
442 assert_eq!(format!("{addr}"), "192.168.1.1:8080");
443 }
444
445 #[test]
446 fn test_parse_ipv6() {
447 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
448 assert!(addr.as_host().is_ipaddr());
449 assert_eq!(addr.as_port().as_u16(), 8080);
450 }
451
452 #[test]
453 fn test_parse_domainname() {
454 let addr: SocketAddr = "example.com:443".parse().unwrap();
455 assert!(addr.as_host().is_domainname());
456 assert_eq!(addr.as_port().as_u16(), 443);
457 assert_eq!(format!("{addr}"), "example.com:443");
458 }
459
460 #[test]
461 fn test_parse_domainname_with_digits() {
462 let addr: SocketAddr = "123.example.com:443".parse().unwrap();
463 assert!(addr.as_host().is_domainname());
464 assert_eq!(addr.as_port().as_u16(), 443);
465 }
466
467 #[test]
468 fn test_parse_str_empty() {
469 assert!(SocketAddr::parse_str("").is_err());
470 }
471
472 #[test]
473 fn test_parse_str_missing_separator() {
474 assert!(SocketAddr::parse_str("192.168.1.1").is_err());
475 }
476
477 #[test]
478 fn test_parse_str_empty_host() {
479 assert!(SocketAddr::parse_str(":8080").is_err());
480 }
481
482 #[test]
483 fn test_parse_str_empty_port() {
484 assert!(SocketAddr::parse_str("192.168.1.1:").is_err());
485 }
486
487 #[test]
488 fn test_parse_str_invalid_host() {
489 assert!(SocketAddr::parse_str("-invalid:8080").is_err());
490 }
491
492 #[test]
493 fn test_parse_str_invalid_port() {
494 assert!(SocketAddr::parse_str("192.168.1.1:invalid").is_err());
495 }
496
497 #[test]
498 fn test_parse_str_port_zero() {
499 assert!(SocketAddr::parse_str("192.168.1.1:0").is_err());
500 }
501
502 #[test]
503 fn test_parse_str_port_out_of_range() {
504 assert!(SocketAddr::parse_str("192.168.1.1:99999").is_err());
505 }
506
507 #[test]
508 fn test_set_host() {
509 let mut addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
510 let new_host = Host::parse_str("10.0.0.1").unwrap();
511 addr.set_host(new_host);
512 assert_eq!(format!("{addr}"), "10.0.0.1:8080");
513 }
514
515 #[test]
516 fn test_set_port() {
517 let mut addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
518 let new_port = Port::new(9090).unwrap();
519 addr.set_port(new_port);
520 assert_eq!(format!("{addr}"), "192.168.1.1:9090");
521 }
522
523 #[test]
524 fn test_into_parts() {
525 let host = Host::parse_str("192.168.1.1").unwrap();
526 let port = Port::new(8080).unwrap();
527 let addr = SocketAddr::new(host, port);
528
529 let (h, p) = addr.into_parts();
530 assert!(h.is_ipaddr());
531 assert_eq!(p.as_u16(), 8080);
532 }
533
534 #[test]
535 fn test_as_host_and_as_port() {
536 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
537 assert!(addr.as_host().is_ipaddr());
538 assert_eq!(addr.as_port().as_u16(), 8080);
539 }
540
541 #[test]
542 fn test_is_ip() {
543 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
544 assert!(addr.is_ip());
545 assert!(!addr.is_domain_name());
546 assert!(!addr.is_hostname());
547 }
548
549 #[test]
550 fn test_is_domain_name() {
551 let addr: SocketAddr = "example.com:443".parse().unwrap();
552 assert!(!addr.is_ip());
553 assert!(addr.is_domain_name());
554 assert!(!addr.is_hostname());
555 }
556
557 #[test]
558 fn test_host_is_domainname() {
559 let addr: SocketAddr = "example.com:443".parse().unwrap();
560 assert!(addr.as_host().is_domainname());
561 }
562
563 #[test]
564 fn test_socket_addr_is_domain_name() {
565 let addr: SocketAddr = "example.com:443".parse().unwrap();
566 assert!(addr.is_domain_name());
567 }
568
569 #[test]
570 fn test_is_hostname() {
571 let hostname = crate::net::Hostname::new("localhost").unwrap();
572 let host = Host::from_hostname(hostname);
573 let port = Port::new(3000).unwrap();
574 let addr = SocketAddr::new(host, port);
575 assert!(!addr.is_ip());
576 assert!(!addr.is_domain_name());
577 assert!(addr.is_hostname());
578 }
579
580 #[test]
581 fn test_equality() {
582 let addr1: SocketAddr = "192.168.1.1:8080".parse().unwrap();
583 let addr2: SocketAddr = "192.168.1.1:8080".parse().unwrap();
584 let addr3: SocketAddr = "192.168.1.1:9090".parse().unwrap();
585 let addr4: SocketAddr = "10.0.0.1:8080".parse().unwrap();
586
587 assert_eq!(addr1, addr2);
588 assert_ne!(addr1, addr3);
589 assert_ne!(addr1, addr4);
590 }
591
592 #[test]
593 fn test_clone() {
594 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
595 let addr2 = addr.clone();
596 assert_eq!(addr, addr2);
597 }
598
599 #[test]
600 fn test_display() {
601 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
602 assert_eq!(format!("{addr}"), "192.168.1.1:8080");
603
604 let addr: SocketAddr = "example.com:443".parse().unwrap();
605 assert_eq!(format!("{addr}"), "example.com:443");
606
607 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
609 assert_eq!(format!("{addr}"), "[::1]:8080");
610
611 let addr: SocketAddr = "[2001:db8::1]:443".parse().unwrap();
612 assert_eq!(format!("{addr}"), "[2001:db8::1]:443");
613 }
614
615 #[test]
616 fn test_debug() {
617 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
618 let debug = format!("{:?}", addr);
619 assert!(debug.contains("SocketAddr"));
620 }
621
622 #[test]
623 fn test_hash() {
624 use core::hash::Hash;
625 use core::hash::Hasher;
626
627 #[derive(Default)]
628 struct SimpleHasher(u64);
629
630 impl Hasher for SimpleHasher {
631 fn finish(&self) -> u64 {
632 self.0
633 }
634
635 fn write(&mut self, bytes: &[u8]) {
636 for byte in bytes {
637 self.0 = self.0.wrapping_mul(31).wrapping_add(*byte as u64);
638 }
639 }
640 }
641
642 let addr1: SocketAddr = "192.168.1.1:8080".parse().unwrap();
643 let addr2: SocketAddr = "192.168.1.1:8080".parse().unwrap();
644 let addr3: SocketAddr = "10.0.0.1:8080".parse().unwrap();
645
646 let mut hasher1 = SimpleHasher::default();
647 let mut hasher2 = SimpleHasher::default();
648 let mut hasher3 = SimpleHasher::default();
649
650 addr1.hash(&mut hasher1);
651 addr2.hash(&mut hasher2);
652 addr3.hash(&mut hasher3);
653
654 assert_eq!(hasher1.finish(), hasher2.finish());
655 assert_ne!(hasher1.finish(), hasher3.finish());
656 }
657
658 #[test]
659 fn test_parse_ipv6_with_brackets() {
660 let addr: SocketAddr = "[2001:db8::1]:8080".parse().unwrap();
661 assert!(addr.as_host().is_ipaddr());
662 assert_eq!(addr.as_port().as_u16(), 8080);
663 }
664
665 #[test]
666 fn test_parse_localhost() {
667 let addr: SocketAddr = "localhost:3000".parse().unwrap();
668 assert!(addr.as_host().is_domainname());
669 assert_eq!(addr.as_port().as_u16(), 3000);
670 }
671
672 #[test]
673 fn test_parse_system_port() {
674 let addr: SocketAddr = "192.168.1.1:80".parse().unwrap();
675 assert_eq!(addr.as_port().as_u16(), 80);
676 assert!(addr.as_port().is_system_port());
677 }
678
679 #[test]
680 fn test_parse_registered_port() {
681 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
682 assert_eq!(addr.as_port().as_u16(), 8080);
683 assert!(addr.as_port().is_registered_port());
684 }
685
686 #[test]
687 fn test_parse_dynamic_port() {
688 let addr: SocketAddr = "192.168.1.1:50000".parse().unwrap();
689 assert_eq!(addr.as_port().as_u16(), 50000);
690 assert!(addr.as_port().is_dynamic_port());
691 }
692
693 #[test]
694 fn test_error_display() {
695 let err = SocketAddrError::MissingPortSeparator;
696 assert_eq!(format!("{err}"), "missing port separator ':'");
697
698 let err = SocketAddrError::InvalidHost;
699 assert_eq!(format!("{err}"), "invalid host");
700
701 let err = SocketAddrError::InvalidPort;
702 assert_eq!(format!("{err}"), "invalid port");
703
704 let err = SocketAddrError::EmptyInput;
705 assert_eq!(format!("{err}"), "empty input");
706 }
707
708 #[test]
709 fn test_parse_str_method() {
710 let addr = SocketAddr::parse_str("192.168.1.1:8080").unwrap();
711 assert!(addr.as_host().is_ipaddr());
712 assert_eq!(addr.as_port().as_u16(), 8080);
713 }
714
715 #[test]
716 fn test_case_insensitive() {
717 let addr1: SocketAddr = "EXAMPLE.COM:443".parse().unwrap();
718 let addr2: SocketAddr = "example.com:443".parse().unwrap();
719 assert_eq!(addr1, addr2);
720 }
721
722 #[test]
723 fn test_ipv4_loopback() {
724 let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
725 assert!(addr.as_host().is_ipaddr());
726 }
727
728 #[test]
729 fn test_ipv4_private() {
730 let addr: SocketAddr = "10.0.0.1:8080".parse().unwrap();
731 assert!(addr.as_host().is_ipaddr());
732 }
733
734 #[test]
735 fn test_ipv6_loopback() {
736 let addr: SocketAddr = "[::1]:8080".parse().unwrap();
737 assert!(addr.as_host().is_ipaddr());
738 }
739
740 #[test]
741 fn test_numeric_domainname() {
742 let addr: SocketAddr = "123:8080".parse().unwrap();
743 assert!(addr.as_host().is_domainname());
744 }
745
746 #[test]
747 fn test_multi_label_domainname() {
748 let addr: SocketAddr = "api.v1.example.com:443".parse().unwrap();
749 assert!(addr.as_host().is_domainname());
750 }
751
752 #[test]
753 fn test_max_port() {
754 let addr: SocketAddr = "192.168.1.1:65535".parse().unwrap();
755 assert_eq!(addr.as_port().as_u16(), 65535);
756 }
757
758 #[test]
759 fn test_min_port() {
760 let addr: SocketAddr = "192.168.1.1:1".parse().unwrap();
761 assert_eq!(addr.as_port().as_u16(), 1);
762 }
763
764 #[cfg(feature = "std")]
765 #[test]
766 fn test_try_from_std_socket_addr() {
767 let std_addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
768 let addr = SocketAddr::try_from(std_addr).unwrap();
769 assert!(addr.as_host().is_ipaddr());
770 assert_eq!(addr.as_port().as_u16(), 8080);
771 }
772
773 #[cfg(feature = "std")]
774 #[test]
775 fn test_try_from_std_socket_addr_ipv6() {
776 let std_addr = std::net::SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 1], 8080));
777 let addr = SocketAddr::try_from(std_addr).unwrap();
778 assert!(addr.as_host().is_ipaddr());
779 assert_eq!(addr.as_port().as_u16(), 8080);
780 }
781
782 #[cfg(feature = "std")]
783 #[test]
784 fn test_try_from_std_socket_addr_port_zero_fails() {
785 let std_addr = std::net::SocketAddr::from(([127, 0, 0, 1], 0));
786 let result = SocketAddr::try_from(std_addr);
787 assert!(result.is_err());
788 assert_eq!(result.unwrap_err(), StdConversionError::PortZero);
789 }
790
791 #[cfg(feature = "std")]
792 #[test]
793 fn test_try_to_std_socket_addr_ip() {
794 let addr: SocketAddr = "192.168.1.1:8080".parse().unwrap();
795 let std_addr = std::net::SocketAddr::try_from(addr).unwrap();
796 assert_eq!(std_addr.ip(), std::net::IpAddr::from([192, 168, 1, 1]));
797 assert_eq!(std_addr.port(), 8080);
798 }
799
800 #[cfg(feature = "std")]
801 #[test]
802 fn test_try_to_std_socket_addr_domainname_fails() {
803 let addr: SocketAddr = "example.com:443".parse().unwrap();
804 let result = std::net::SocketAddr::try_from(addr);
805 assert!(result.is_err());
806 assert_eq!(result.unwrap_err(), StdConversionError::NotIpAddress);
807 }
808
809 #[cfg(feature = "std")]
810 #[test]
811 fn test_try_to_std_socket_addr_hostname_fails() {
812 let hostname = crate::net::Hostname::new("localhost").unwrap();
813 let host = Host::from_hostname(hostname);
814 let port = Port::new(3000).unwrap();
815 let addr = SocketAddr::new(host, port);
816 let result = std::net::SocketAddr::try_from(addr);
817 assert!(result.is_err());
818 assert_eq!(result.unwrap_err(), StdConversionError::NotIpAddress);
819 }
820
821 #[cfg(feature = "std")]
822 #[test]
823 fn test_std_conversion_error_display() {
824 let err = StdConversionError::NotIpAddress;
825 assert_eq!(
826 format!("{err}"),
827 "cannot convert to std::net::SocketAddr: host is not an IP address"
828 );
829
830 let err = StdConversionError::PortZero;
831 assert_eq!(
832 format!("{err}"),
833 "cannot convert from std::net::SocketAddr: port 0 is not allowed"
834 );
835 }
836
837 #[cfg(feature = "std")]
838 #[test]
839 fn test_roundtrip_std_socket_addr() {
840 let std_addr1 = std::net::SocketAddr::from(([192, 168, 1, 1], 8080));
841 let addr = SocketAddr::try_from(std_addr1).unwrap();
842 let std_addr2 = std::net::SocketAddr::try_from(addr).unwrap();
843 assert_eq!(std_addr1, std_addr2);
844 }
845}