rustix/backend/libc/net/
read_sockaddr.rs

1//! The BSD sockets API requires us to read the `sa_family` field before we can
2//! interpret the rest of a `sockaddr` produced by the kernel.
3
4#[cfg(unix)]
5use super::addr::SocketAddrUnix;
6use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
7use crate::backend::c;
8#[cfg(not(windows))]
9use crate::ffi::CStr;
10use crate::io::Errno;
11use crate::net::addr::SocketAddrLen;
12#[cfg(linux_kernel)]
13use crate::net::netlink::SocketAddrNetlink;
14#[cfg(target_os = "linux")]
15use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags};
16use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
17use core::mem::size_of;
18
19// This must match the header of `sockaddr`.
20#[repr(C)]
21pub(crate) struct sockaddr_header {
22    #[cfg(any(
23        bsd,
24        target_os = "aix",
25        target_os = "espidf",
26        target_os = "haiku",
27        target_os = "nto",
28        target_os = "vita"
29    ))]
30    sa_len: u8,
31    #[cfg(any(
32        bsd,
33        target_os = "aix",
34        target_os = "espidf",
35        target_os = "haiku",
36        target_os = "nto",
37        target_os = "vita"
38    ))]
39    sa_family: u8,
40    #[cfg(not(any(
41        bsd,
42        target_os = "aix",
43        target_os = "espidf",
44        target_os = "haiku",
45        target_os = "nto",
46        target_os = "vita"
47    )))]
48    sa_family: u16,
49}
50
51/// Read the `sa_family` field from a socket address returned from the OS.
52///
53/// # Safety
54///
55/// `storage` must point to a valid socket address returned from the OS.
56#[inline]
57pub(crate) unsafe fn read_sa_family(storage: *const c::sockaddr) -> u16 {
58    // Assert that we know the layout of `sockaddr`.
59    let _ = c::sockaddr {
60        #[cfg(any(
61            bsd,
62            target_os = "aix",
63            target_os = "espidf",
64            target_os = "haiku",
65            target_os = "hurd",
66            target_os = "nto",
67            target_os = "vita"
68        ))]
69        sa_len: 0_u8,
70        #[cfg(any(
71            bsd,
72            target_os = "aix",
73            target_os = "espidf",
74            target_os = "haiku",
75            target_os = "hurd",
76            target_os = "nto",
77            target_os = "vita"
78        ))]
79        sa_family: 0_u8,
80        #[cfg(not(any(
81            bsd,
82            target_os = "aix",
83            target_os = "espidf",
84            target_os = "haiku",
85            target_os = "hurd",
86            target_os = "nto",
87            target_os = "vita"
88        )))]
89        sa_family: 0_u16,
90        #[cfg(not(any(target_os = "haiku", target_os = "horizon")))]
91        sa_data: [0; 14],
92        #[cfg(target_os = "horizon")]
93        sa_data: [0; 26],
94        #[cfg(target_os = "haiku")]
95        sa_data: [0; 30],
96    };
97
98    (*storage.cast::<sockaddr_header>()).sa_family.into()
99}
100
101/// Read the first byte of the `sun_path` field, assuming we have an `AF_UNIX`
102/// socket address.
103#[cfg(apple)]
104#[inline]
105unsafe fn read_sun_path0(storage: *const c::sockaddr) -> u8 {
106    // In `read_sa_family` we assert that we know the layout of `sockaddr`.
107    storage
108        .cast::<u8>()
109        .add(super::addr::offsetof_sun_path())
110        .read()
111}
112
113/// Check if a socket address returned from the OS is considered non-empty.
114///
115/// # Safety
116///
117/// `storage` must point to a least an initialized `sockaddr_header`.
118#[inline]
119pub(crate) unsafe fn sockaddr_nonempty(storage: *const c::sockaddr, len: SocketAddrLen) -> bool {
120    if len == 0 {
121        return false;
122    }
123
124    assert!(len as usize >= size_of::<c::sa_family_t>());
125    let family: c::c_int = read_sa_family(storage.cast::<c::sockaddr>()).into();
126    if family == c::AF_UNSPEC {
127        return false;
128    }
129
130    // On macOS, if we get an `AF_UNIX` with an empty path, treat it as an
131    // absent address.
132    #[cfg(apple)]
133    if family == c::AF_UNIX && read_sun_path0(storage) == 0 {
134        return false;
135    }
136
137    true
138}
139
140/// Set the `sa_family` field of a socket address to `AF_UNSPEC`, so that we
141/// can test for `AF_UNSPEC` to test whether it was stored to.
142///
143/// # Safety
144///
145/// `storage` must point to a least an initialized `sockaddr_header`.
146pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) {
147    (*storage.cast::<sockaddr_header>()).sa_family = c::AF_UNSPEC as _;
148}
149
150#[inline]
151pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result<SocketAddrV4, Errno> {
152    if addr.address_family() != AddressFamily::INET {
153        return Err(Errno::AFNOSUPPORT);
154    }
155    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in>());
156    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in>() };
157    Ok(SocketAddrV4::new(
158        Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
159        u16::from_be(decode.sin_port),
160    ))
161}
162
163#[inline]
164pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result<SocketAddrV6, Errno> {
165    if addr.address_family() != AddressFamily::INET6 {
166        return Err(Errno::AFNOSUPPORT);
167    }
168    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in6>());
169    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in6>() };
170    Ok(SocketAddrV6::new(
171        Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
172        u16::from_be(decode.sin6_port),
173        u32::from_be(decode.sin6_flowinfo),
174        sockaddr_in6_sin6_scope_id(decode),
175    ))
176}
177
178#[cfg(unix)]
179#[inline]
180pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result<SocketAddrUnix, Errno> {
181    if addr.address_family() != AddressFamily::UNIX {
182        return Err(Errno::AFNOSUPPORT);
183    }
184
185    let offsetof_sun_path = super::addr::offsetof_sun_path();
186    let len = addr.addr_len() as usize;
187
188    assert!(len >= offsetof_sun_path);
189
190    if len == offsetof_sun_path {
191        SocketAddrUnix::new(&[][..])
192    } else {
193        let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_un>() };
194
195        // On Linux check for Linux's [abstract namespace].
196        //
197        // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
198        #[cfg(linux_kernel)]
199        if decode.sun_path[0] == 0 {
200            let name = &decode.sun_path[1..len - offsetof_sun_path];
201            let name = unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(name) };
202            return SocketAddrUnix::new_abstract_name(name);
203        }
204
205        // Otherwise we expect a NUL-terminated filesystem path.
206
207        // Trim off unused bytes from the end of `path_bytes`.
208        let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) {
209            // FreeBSD and illumos sometimes set the length to longer
210            // than the length of the NUL-terminated string. Find the
211            // NUL and truncate the string accordingly.
212            &decode.sun_path[..decode
213                .sun_path
214                .iter()
215                .position(|b| *b == 0)
216                .ok_or(Errno::INVAL)?]
217        } else {
218            // Otherwise, use the provided length.
219            let provided_len = len - 1 - offsetof_sun_path;
220            if decode.sun_path[provided_len] != 0 {
221                return Err(Errno::INVAL);
222            }
223            debug_assert_eq!(
224                unsafe { CStr::from_ptr(decode.sun_path.as_ptr().cast()) }
225                    .to_bytes()
226                    .len(),
227                provided_len
228            );
229            &decode.sun_path[..provided_len]
230        };
231
232        SocketAddrUnix::new(unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes) })
233    }
234}
235
236#[cfg(target_os = "linux")]
237#[inline]
238pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result<SocketAddrXdp, Errno> {
239    if addr.address_family() != AddressFamily::XDP {
240        return Err(Errno::AFNOSUPPORT);
241    }
242    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_xdp>());
243    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_xdp>() };
244
245    // This ignores the `sxdp_shared_umem_fd` field, which is only expected to
246    // be significant in `bind` calls, and not returned from `acceptfrom` or
247    // `recvmsg` or similar.
248    Ok(SocketAddrXdp::new(
249        SocketAddrXdpFlags::from_bits_retain(decode.sxdp_flags),
250        u32::from_be(decode.sxdp_ifindex),
251        u32::from_be(decode.sxdp_queue_id),
252    ))
253}
254
255#[cfg(linux_kernel)]
256#[inline]
257pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result<SocketAddrNetlink, Errno> {
258    if addr.address_family() != AddressFamily::NETLINK {
259        return Err(Errno::AFNOSUPPORT);
260    }
261    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_nl>());
262    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_nl>() };
263    Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups))
264}