1 //! The BSD sockets API requires us to read the `ss_family` field before we can
2 //! interpret the rest of a `sockaddr` produced by the kernel.
3
4 #[cfg(unix)]
5 use super::addr::SocketAddrUnix;
6 use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
7 use crate::backend::c;
8 #[cfg(not(windows))]
9 use crate::ffi::CStr;
10 use crate::io;
11 #[cfg(target_os = "linux")]
12 use crate::net::xdp::{SockaddrXdpFlags, SocketAddrXdp};
13 use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
14 use core::mem::size_of;
15
16 // This must match the header of `sockaddr`.
17 #[repr(C)]
18 struct sockaddr_header {
19 #[cfg(any(
20 bsd,
21 target_os = "aix",
22 target_os = "espidf",
23 target_os = "haiku",
24 target_os = "nto",
25 target_os = "vita"
26 ))]
27 sa_len: u8,
28 #[cfg(any(
29 bsd,
30 target_os = "aix",
31 target_os = "espidf",
32 target_os = "haiku",
33 target_os = "nto",
34 target_os = "vita"
35 ))]
36 ss_family: u8,
37 #[cfg(not(any(
38 bsd,
39 target_os = "aix",
40 target_os = "espidf",
41 target_os = "haiku",
42 target_os = "nto",
43 target_os = "vita"
44 )))]
45 ss_family: u16,
46 }
47
48 /// Read the `ss_family` field from a socket address returned from the OS.
49 ///
50 /// # Safety
51 ///
52 /// `storage` must point to a valid socket address returned from the OS.
53 #[inline]
read_ss_family(storage: *const c::sockaddr_storage) -> u1654 unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 {
55 // Assert that we know the layout of `sockaddr`.
56 let _ = c::sockaddr {
57 #[cfg(any(
58 bsd,
59 target_os = "aix",
60 target_os = "espidf",
61 target_os = "haiku",
62 target_os = "nto",
63 target_os = "vita"
64 ))]
65 sa_len: 0_u8,
66 #[cfg(any(
67 bsd,
68 target_os = "aix",
69 target_os = "espidf",
70 target_os = "haiku",
71 target_os = "nto",
72 target_os = "vita"
73 ))]
74 sa_family: 0_u8,
75 #[cfg(not(any(
76 bsd,
77 target_os = "aix",
78 target_os = "espidf",
79 target_os = "haiku",
80 target_os = "nto",
81 target_os = "vita"
82 )))]
83 sa_family: 0_u16,
84 #[cfg(not(target_os = "haiku"))]
85 sa_data: [0; 14],
86 #[cfg(target_os = "haiku")]
87 sa_data: [0; 30],
88 };
89
90 (*storage.cast::<sockaddr_header>()).ss_family.into()
91 }
92
93 /// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we
94 /// can test for `AF_UNSPEC` to test whether it was stored to.
initialize_family_to_unspec(storage: *mut c::sockaddr_storage)95 pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) {
96 (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _;
97 }
98
99 /// Read a socket address encoded in a platform-specific format.
100 ///
101 /// # Safety
102 ///
103 /// `storage` must point to valid socket address storage.
read_sockaddr( storage: *const c::sockaddr_storage, len: usize, ) -> io::Result<SocketAddrAny>104 pub(crate) unsafe fn read_sockaddr(
105 storage: *const c::sockaddr_storage,
106 len: usize,
107 ) -> io::Result<SocketAddrAny> {
108 #[cfg(unix)]
109 let offsetof_sun_path = super::addr::offsetof_sun_path();
110
111 if len < size_of::<c::sa_family_t>() {
112 return Err(io::Errno::INVAL);
113 }
114 match read_ss_family(storage).into() {
115 c::AF_INET => {
116 if len < size_of::<c::sockaddr_in>() {
117 return Err(io::Errno::INVAL);
118 }
119 let decode = &*storage.cast::<c::sockaddr_in>();
120 Ok(SocketAddrAny::V4(SocketAddrV4::new(
121 Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
122 u16::from_be(decode.sin_port),
123 )))
124 }
125 c::AF_INET6 => {
126 if len < size_of::<c::sockaddr_in6>() {
127 return Err(io::Errno::INVAL);
128 }
129 let decode = &*storage.cast::<c::sockaddr_in6>();
130 #[cfg(not(windows))]
131 let s6_addr = decode.sin6_addr.s6_addr;
132 #[cfg(windows)]
133 let s6_addr = decode.sin6_addr.u.Byte;
134 #[cfg(not(windows))]
135 let sin6_scope_id = decode.sin6_scope_id;
136 #[cfg(windows)]
137 let sin6_scope_id = decode.Anonymous.sin6_scope_id;
138 Ok(SocketAddrAny::V6(SocketAddrV6::new(
139 Ipv6Addr::from(s6_addr),
140 u16::from_be(decode.sin6_port),
141 u32::from_be(decode.sin6_flowinfo),
142 sin6_scope_id,
143 )))
144 }
145 #[cfg(unix)]
146 c::AF_UNIX => {
147 if len < offsetof_sun_path {
148 return Err(io::Errno::INVAL);
149 }
150 if len == offsetof_sun_path {
151 SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix)
152 } else {
153 let decode = &*storage.cast::<c::sockaddr_un>();
154
155 // On Linux check for Linux's [abstract namespace].
156 //
157 // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
158 #[cfg(linux_kernel)]
159 if decode.sun_path[0] == 0 {
160 return SocketAddrUnix::new_abstract_name(core::mem::transmute::<
161 &[c::c_char],
162 &[u8],
163 >(
164 &decode.sun_path[1..len - offsetof_sun_path],
165 ))
166 .map(SocketAddrAny::Unix);
167 }
168
169 // Otherwise we expect a NUL-terminated filesystem path.
170
171 // Trim off unused bytes from the end of `path_bytes`.
172 let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) {
173 // FreeBSD and illumos sometimes set the length to longer
174 // than the length of the NUL-terminated string. Find the
175 // NUL and truncate the string accordingly.
176 &decode.sun_path[..decode
177 .sun_path
178 .iter()
179 .position(|b| *b == 0)
180 .ok_or(io::Errno::INVAL)?]
181 } else {
182 // Otherwise, use the provided length.
183 let provided_len = len - 1 - offsetof_sun_path;
184 if decode.sun_path[provided_len] != 0 {
185 return Err(io::Errno::INVAL);
186 }
187 debug_assert_eq!(
188 CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(),
189 provided_len
190 );
191 &decode.sun_path[..provided_len]
192 };
193
194 SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
195 .map(SocketAddrAny::Unix)
196 }
197 }
198 #[cfg(target_os = "linux")]
199 c::AF_XDP => {
200 if len < size_of::<c::sockaddr_xdp>() {
201 return Err(io::Errno::INVAL);
202 }
203 let decode = &*storage.cast::<c::sockaddr_xdp>();
204 Ok(SocketAddrAny::Xdp(SocketAddrXdp::new(
205 SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
206 u32::from_be(decode.sxdp_ifindex),
207 u32::from_be(decode.sxdp_queue_id),
208 u32::from_be(decode.sxdp_shared_umem_fd),
209 )))
210 }
211 _ => Err(io::Errno::INVAL),
212 }
213 }
214
215 /// Read an optional socket address returned from the OS.
216 ///
217 /// # Safety
218 ///
219 /// `storage` must point to a valid socket address returned from the OS.
maybe_read_sockaddr_os( storage: *const c::sockaddr_storage, len: usize, ) -> Option<SocketAddrAny>220 pub(crate) unsafe fn maybe_read_sockaddr_os(
221 storage: *const c::sockaddr_storage,
222 len: usize,
223 ) -> Option<SocketAddrAny> {
224 if len == 0 {
225 return None;
226 }
227
228 assert!(len >= size_of::<c::sa_family_t>());
229 let family = read_ss_family(storage).into();
230 if family == c::AF_UNSPEC {
231 None
232 } else {
233 Some(inner_read_sockaddr_os(family, storage, len))
234 }
235 }
236
237 /// Read a socket address returned from the OS.
238 ///
239 /// # Safety
240 ///
241 /// `storage` must point to a valid socket address returned from the OS.
read_sockaddr_os( storage: *const c::sockaddr_storage, len: usize, ) -> SocketAddrAny242 pub(crate) unsafe fn read_sockaddr_os(
243 storage: *const c::sockaddr_storage,
244 len: usize,
245 ) -> SocketAddrAny {
246 assert!(len >= size_of::<c::sa_family_t>());
247 let family = read_ss_family(storage).into();
248 inner_read_sockaddr_os(family, storage, len)
249 }
250
inner_read_sockaddr_os( family: c::c_int, storage: *const c::sockaddr_storage, len: usize, ) -> SocketAddrAny251 unsafe fn inner_read_sockaddr_os(
252 family: c::c_int,
253 storage: *const c::sockaddr_storage,
254 len: usize,
255 ) -> SocketAddrAny {
256 #[cfg(unix)]
257 let offsetof_sun_path = super::addr::offsetof_sun_path();
258
259 assert!(len >= size_of::<c::sa_family_t>());
260 match family {
261 c::AF_INET => {
262 assert!(len >= size_of::<c::sockaddr_in>());
263 let decode = &*storage.cast::<c::sockaddr_in>();
264 SocketAddrAny::V4(SocketAddrV4::new(
265 Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
266 u16::from_be(decode.sin_port),
267 ))
268 }
269 c::AF_INET6 => {
270 assert!(len >= size_of::<c::sockaddr_in6>());
271 let decode = &*storage.cast::<c::sockaddr_in6>();
272 SocketAddrAny::V6(SocketAddrV6::new(
273 Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
274 u16::from_be(decode.sin6_port),
275 u32::from_be(decode.sin6_flowinfo),
276 sockaddr_in6_sin6_scope_id(decode),
277 ))
278 }
279 #[cfg(unix)]
280 c::AF_UNIX => {
281 assert!(len >= offsetof_sun_path);
282 if len == offsetof_sun_path {
283 SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
284 } else {
285 let decode = &*storage.cast::<c::sockaddr_un>();
286
287 // On Linux check for Linux's [abstract namespace].
288 //
289 // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
290 #[cfg(linux_kernel)]
291 if decode.sun_path[0] == 0 {
292 return SocketAddrAny::Unix(
293 SocketAddrUnix::new_abstract_name(core::mem::transmute::<
294 &[c::c_char],
295 &[u8],
296 >(
297 &decode.sun_path[1..len - offsetof_sun_path],
298 ))
299 .unwrap(),
300 );
301 }
302
303 // Otherwise we expect a NUL-terminated filesystem path.
304 assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
305 let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
306
307 // FreeBSD and illumos sometimes set the length to longer than
308 // the length of the NUL-terminated string. Find the NUL and
309 // truncate the string accordingly.
310 #[cfg(any(solarish, target_os = "freebsd"))]
311 let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
312
313 SocketAddrAny::Unix(
314 SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
315 .unwrap(),
316 )
317 }
318 }
319 #[cfg(target_os = "linux")]
320 c::AF_XDP => {
321 assert!(len >= size_of::<c::sockaddr_xdp>());
322 let decode = &*storage.cast::<c::sockaddr_xdp>();
323 SocketAddrAny::Xdp(SocketAddrXdp::new(
324 SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
325 u32::from_be(decode.sxdp_ifindex),
326 u32::from_be(decode.sxdp_queue_id),
327 u32::from_be(decode.sxdp_shared_umem_fd),
328 ))
329 }
330 other => unimplemented!("{:?}", other),
331 }
332 }
333