]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use super::super::c; |
2 | #[cfg(unix)] | |
3 | use super::addr::SocketAddrUnix; | |
4 | use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id}; | |
5 | #[cfg(not(windows))] | |
6 | use crate::ffi::CStr; | |
7 | use crate::io; | |
8 | use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; | |
9 | #[cfg(not(windows))] | |
10 | use alloc::vec::Vec; | |
11 | use core::mem::size_of; | |
12 | ||
13 | // This must match the header of `sockaddr`. | |
14 | #[repr(C)] | |
15 | struct sockaddr_header { | |
353b0b11 | 16 | #[cfg(any(bsd, target_os = "haiku"))] |
064997fb | 17 | sa_len: u8, |
353b0b11 | 18 | #[cfg(any(bsd, target_os = "haiku"))] |
064997fb | 19 | ss_family: u8, |
353b0b11 | 20 | #[cfg(not(any(bsd, target_os = "haiku")))] |
064997fb FG |
21 | ss_family: u16, |
22 | } | |
23 | ||
24 | #[inline] | |
25 | unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { | |
26 | // Assert that we know the layout of `sockaddr`. | |
27 | let _ = c::sockaddr { | |
353b0b11 | 28 | #[cfg(any(bsd, target_os = "haiku"))] |
064997fb | 29 | sa_len: 0_u8, |
353b0b11 | 30 | #[cfg(any(bsd, target_os = "haiku"))] |
064997fb | 31 | sa_family: 0_u8, |
353b0b11 | 32 | #[cfg(not(any(bsd, target_os = "haiku")))] |
064997fb | 33 | sa_family: 0_u16, |
487cf647 | 34 | #[cfg(not(target_os = "haiku"))] |
064997fb | 35 | sa_data: [0; 14], |
487cf647 FG |
36 | #[cfg(target_os = "haiku")] |
37 | sa_data: [0; 30], | |
064997fb FG |
38 | }; |
39 | ||
40 | (*storage.cast::<sockaddr_header>()).ss_family.into() | |
41 | } | |
42 | ||
43 | /// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we | |
44 | /// can test for `AF_UNSPEC` to test whether it was stored to. | |
45 | pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) { | |
46 | (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _; | |
47 | } | |
48 | ||
49 | pub(crate) unsafe fn read_sockaddr( | |
50 | storage: *const c::sockaddr_storage, | |
51 | len: usize, | |
52 | ) -> io::Result<SocketAddrAny> { | |
53 | #[cfg(unix)] | |
54 | let offsetof_sun_path = super::addr::offsetof_sun_path(); | |
55 | ||
56 | if len < size_of::<c::sa_family_t>() { | |
57 | return Err(io::Errno::INVAL); | |
58 | } | |
59 | match read_ss_family(storage).into() { | |
60 | c::AF_INET => { | |
61 | if len < size_of::<c::sockaddr_in>() { | |
62 | return Err(io::Errno::INVAL); | |
63 | } | |
64 | let decode = *storage.cast::<c::sockaddr_in>(); | |
65 | Ok(SocketAddrAny::V4(SocketAddrV4::new( | |
66 | Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), | |
67 | u16::from_be(decode.sin_port), | |
68 | ))) | |
69 | } | |
70 | c::AF_INET6 => { | |
71 | if len < size_of::<c::sockaddr_in6>() { | |
72 | return Err(io::Errno::INVAL); | |
73 | } | |
74 | let decode = *storage.cast::<c::sockaddr_in6>(); | |
75 | #[cfg(not(windows))] | |
76 | let s6_addr = decode.sin6_addr.s6_addr; | |
77 | #[cfg(windows)] | |
78 | let s6_addr = decode.sin6_addr.u.Byte; | |
79 | #[cfg(not(windows))] | |
80 | let sin6_scope_id = decode.sin6_scope_id; | |
81 | #[cfg(windows)] | |
82 | let sin6_scope_id = decode.Anonymous.sin6_scope_id; | |
83 | Ok(SocketAddrAny::V6(SocketAddrV6::new( | |
84 | Ipv6Addr::from(s6_addr), | |
85 | u16::from_be(decode.sin6_port), | |
86 | u32::from_be(decode.sin6_flowinfo), | |
87 | sin6_scope_id, | |
88 | ))) | |
89 | } | |
90 | #[cfg(unix)] | |
91 | c::AF_UNIX => { | |
92 | if len < offsetof_sun_path { | |
93 | return Err(io::Errno::INVAL); | |
94 | } | |
95 | if len == offsetof_sun_path { | |
96 | Ok(SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())) | |
97 | } else { | |
98 | let decode = *storage.cast::<c::sockaddr_un>(); | |
99 | ||
100 | // Trim off unused bytes from the end of `path_bytes`. | |
101 | let path_bytes = if cfg!(target_os = "freebsd") { | |
102 | // FreeBSD sometimes sets the length to longer than the length | |
103 | // of the NUL-terminated string. Find the NUL and truncate the | |
104 | // string accordingly. | |
105 | &decode.sun_path[..decode.sun_path.iter().position(|b| *b == 0).unwrap()] | |
106 | } else { | |
107 | // Otherwise, use the provided length. | |
108 | let provided_len = len - 1 - offsetof_sun_path; | |
109 | if decode.sun_path[provided_len] != b'\0' as c::c_char { | |
110 | return Err(io::Errno::INVAL); | |
111 | } | |
112 | debug_assert_eq!( | |
113 | CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(), | |
114 | provided_len | |
115 | ); | |
116 | &decode.sun_path[..provided_len] | |
117 | }; | |
118 | ||
119 | Ok(SocketAddrAny::Unix( | |
120 | SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>()) | |
121 | .unwrap(), | |
122 | )) | |
123 | } | |
124 | } | |
125 | _ => Err(io::Errno::INVAL), | |
126 | } | |
127 | } | |
128 | ||
129 | pub(crate) unsafe fn maybe_read_sockaddr_os( | |
130 | storage: *const c::sockaddr_storage, | |
131 | len: usize, | |
132 | ) -> Option<SocketAddrAny> { | |
133 | if len == 0 { | |
134 | return None; | |
135 | } | |
136 | ||
137 | assert!(len >= size_of::<c::sa_family_t>()); | |
138 | let family = read_ss_family(storage).into(); | |
139 | if family == c::AF_UNSPEC { | |
140 | None | |
141 | } else { | |
142 | Some(inner_read_sockaddr_os(family, storage, len)) | |
143 | } | |
144 | } | |
145 | ||
146 | pub(crate) unsafe fn read_sockaddr_os( | |
147 | storage: *const c::sockaddr_storage, | |
148 | len: usize, | |
149 | ) -> SocketAddrAny { | |
150 | assert!(len >= size_of::<c::sa_family_t>()); | |
151 | let family = read_ss_family(storage).into(); | |
152 | inner_read_sockaddr_os(family, storage, len) | |
153 | } | |
154 | ||
155 | unsafe fn inner_read_sockaddr_os( | |
156 | family: c::c_int, | |
157 | storage: *const c::sockaddr_storage, | |
158 | len: usize, | |
159 | ) -> SocketAddrAny { | |
160 | #[cfg(unix)] | |
161 | let offsetof_sun_path = super::addr::offsetof_sun_path(); | |
162 | ||
163 | assert!(len >= size_of::<c::sa_family_t>()); | |
164 | match family { | |
165 | c::AF_INET => { | |
166 | assert!(len >= size_of::<c::sockaddr_in>()); | |
167 | let decode = *storage.cast::<c::sockaddr_in>(); | |
168 | SocketAddrAny::V4(SocketAddrV4::new( | |
169 | Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), | |
170 | u16::from_be(decode.sin_port), | |
171 | )) | |
172 | } | |
173 | c::AF_INET6 => { | |
174 | assert!(len >= size_of::<c::sockaddr_in6>()); | |
175 | let decode = *storage.cast::<c::sockaddr_in6>(); | |
176 | SocketAddrAny::V6(SocketAddrV6::new( | |
177 | Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)), | |
178 | u16::from_be(decode.sin6_port), | |
179 | u32::from_be(decode.sin6_flowinfo), | |
180 | sockaddr_in6_sin6_scope_id(decode), | |
181 | )) | |
182 | } | |
183 | #[cfg(unix)] | |
184 | c::AF_UNIX => { | |
185 | assert!(len >= offsetof_sun_path); | |
186 | if len == offsetof_sun_path { | |
187 | SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap()) | |
188 | } else { | |
189 | let decode = *storage.cast::<c::sockaddr_un>(); | |
190 | assert_eq!( | |
191 | decode.sun_path[len - 1 - offsetof_sun_path], | |
192 | b'\0' as c::c_char | |
193 | ); | |
194 | let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path]; | |
195 | ||
196 | // FreeBSD sometimes sets the length to longer than the length | |
197 | // of the NUL-terminated string. Find the NUL and truncate the | |
198 | // string accordingly. | |
199 | #[cfg(target_os = "freebsd")] | |
200 | let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()]; | |
201 | ||
202 | SocketAddrAny::Unix( | |
203 | SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>()) | |
204 | .unwrap(), | |
205 | ) | |
206 | } | |
207 | } | |
208 | other => unimplemented!("{:?}", other), | |
209 | } | |
210 | } |