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