1use std::io;
10use std::mem;
11use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
12
13pub fn sockaddr_to_std(
21 storage: &libc::sockaddr_storage,
22 len: libc::socklen_t,
23) -> io::Result<SocketAddr> {
24 let len = usize::try_from(len).unwrap_or(0);
25 let family = i32::from(storage.ss_family);
26
27 if family == libc::AF_INET {
28 if len < mem::size_of::<libc::sockaddr_in>() {
29 return Err(io::Error::other("sockaddr_in shorter than expected"));
30 }
31 let sin = unsafe {
37 std::ptr::read_unaligned(std::ptr::from_ref(storage).cast::<libc::sockaddr_in>())
38 };
39 let ip = Ipv4Addr::from(sin.sin_addr.s_addr.to_ne_bytes());
40 let port = u16::from_be(sin.sin_port);
41 return Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)));
42 }
43
44 if family == libc::AF_INET6 {
45 if len < mem::size_of::<libc::sockaddr_in6>() {
46 return Err(io::Error::other("sockaddr_in6 shorter than expected"));
47 }
48 let sin6 = unsafe {
50 std::ptr::read_unaligned(std::ptr::from_ref(storage).cast::<libc::sockaddr_in6>())
51 };
52 let ip = Ipv6Addr::from(sin6.sin6_addr.s6_addr);
53 let port = u16::from_be(sin6.sin6_port);
54 return Ok(SocketAddr::V6(SocketAddrV6::new(
55 ip,
56 port,
57 sin6.sin6_flowinfo,
58 sin6.sin6_scope_id,
59 )));
60 }
61
62 Err(io::Error::other(format!(
63 "unsupported sockaddr family: {family}"
64 )))
65}
66
67pub fn std_to_sockaddr(addr: SocketAddr) -> (libc::sockaddr_storage, libc::socklen_t) {
70 let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
73
74 match addr {
75 SocketAddr::V4(v4) => {
76 let sin = libc::sockaddr_in {
77 sin_family: libc::sa_family_t::try_from(libc::AF_INET)
78 .expect("AF_INET fits sa_family_t"),
79 sin_port: v4.port().to_be(),
80 sin_addr: libc::in_addr {
81 s_addr: u32::from_ne_bytes(v4.ip().octets()),
82 },
83 sin_zero: [0; 8],
84 };
85 unsafe {
89 std::ptr::write_unaligned(
90 std::ptr::from_mut(&mut storage).cast::<libc::sockaddr_in>(),
91 sin,
92 );
93 }
94 let len = libc::socklen_t::try_from(mem::size_of::<libc::sockaddr_in>())
95 .expect("size_of::<sockaddr_in>() fits socklen_t");
96 (storage, len)
97 }
98 SocketAddr::V6(v6) => {
99 let sin6 = libc::sockaddr_in6 {
100 sin6_family: libc::sa_family_t::try_from(libc::AF_INET6)
101 .expect("AF_INET6 fits sa_family_t"),
102 sin6_port: v6.port().to_be(),
103 sin6_flowinfo: v6.flowinfo(),
104 sin6_addr: libc::in6_addr {
105 s6_addr: v6.ip().octets(),
106 },
107 sin6_scope_id: v6.scope_id(),
108 };
109 unsafe {
111 std::ptr::write_unaligned(
112 std::ptr::from_mut(&mut storage).cast::<libc::sockaddr_in6>(),
113 sin6,
114 );
115 }
116 let len = libc::socklen_t::try_from(mem::size_of::<libc::sockaddr_in6>())
117 .expect("size_of::<sockaddr_in6>() fits socklen_t");
118 (storage, len)
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn v4_roundtrips() {
129 let addr: SocketAddr = "203.0.113.7:51820".parse().unwrap();
130 let (storage, len) = std_to_sockaddr(addr);
131 let back = sockaddr_to_std(&storage, len).unwrap();
132 assert_eq!(back, addr);
133 }
134
135 #[test]
136 fn v4_loopback_roundtrips() {
137 let addr: SocketAddr = "127.0.0.1:9".parse().unwrap();
138 let (storage, len) = std_to_sockaddr(addr);
139 let back = sockaddr_to_std(&storage, len).unwrap();
140 assert_eq!(back, addr);
141 }
142
143 #[test]
144 fn v6_roundtrips() {
145 let addr: SocketAddr = "[2001:db8::1]:443".parse().unwrap();
146 let (storage, len) = std_to_sockaddr(addr);
147 let back = sockaddr_to_std(&storage, len).unwrap();
148 assert_eq!(back, addr);
149 }
150
151 #[test]
152 fn truncated_length_is_rejected() {
153 let addr: SocketAddr = "203.0.113.7:51820".parse().unwrap();
154 let (storage, _) = std_to_sockaddr(addr);
155 let too_short = libc::socklen_t::try_from(2usize).unwrap();
156 assert!(sockaddr_to_std(&storage, too_short).is_err());
157 }
158
159 #[test]
160 fn unknown_family_is_rejected() {
161 let storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
164 let len = libc::socklen_t::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
165 assert!(sockaddr_to_std(&storage, len).is_err());
166 }
167}