rustix/backend/linux_raw/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#![allow(unsafe_code)]
4
5use crate::backend::c;
6use crate::io::Errno;
7use crate::net::addr::SocketAddrLen;
8use crate::net::netlink::SocketAddrNetlink;
9#[cfg(target_os = "linux")]
10use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags};
11use crate::net::{
12    AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrUnix, SocketAddrV4, SocketAddrV6,
13};
14use core::mem::size_of;
15use core::slice;
16
17// This must match the header of `sockaddr`.
18#[repr(C)]
19pub(crate) struct sockaddr_header {
20    sa_family: u16,
21}
22
23/// Read the `sa_family` field from a socket address returned from the OS.
24///
25/// # Safety
26///
27/// `storage` must point to a least an initialized `sockaddr_header`.
28#[inline]
29pub(crate) const unsafe fn read_sa_family(storage: *const c::sockaddr) -> u16 {
30    // Assert that we know the layout of `sockaddr`.
31    let _ = c::sockaddr {
32        __storage: c::sockaddr_storage {
33            __bindgen_anon_1: linux_raw_sys::net::__kernel_sockaddr_storage__bindgen_ty_1 {
34                __bindgen_anon_1:
35                    linux_raw_sys::net::__kernel_sockaddr_storage__bindgen_ty_1__bindgen_ty_1 {
36                        ss_family: 0_u16,
37                        __data: [0; 126_usize],
38                    },
39            },
40        },
41    };
42
43    (*storage.cast::<sockaddr_header>()).sa_family
44}
45
46/// Set the `sa_family` field of a socket address to `AF_UNSPEC`, so that we
47/// can test for `AF_UNSPEC` to test whether it was stored to.
48///
49/// # Safety
50///
51/// `storage` must point to a least an initialized `sockaddr_header`.
52#[inline]
53pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) {
54    (*storage.cast::<sockaddr_header>()).sa_family = c::AF_UNSPEC as _;
55}
56
57/// Check if a socket address returned from the OS is considered non-empty.
58#[inline]
59pub(crate) unsafe fn sockaddr_nonempty(_storage: *const c::sockaddr, len: SocketAddrLen) -> bool {
60    len != 0
61}
62
63#[inline]
64pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result<SocketAddrV4, Errno> {
65    if addr.address_family() != AddressFamily::INET {
66        return Err(Errno::AFNOSUPPORT);
67    }
68    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in>());
69    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in>() };
70    Ok(SocketAddrV4::new(
71        Ipv4Addr::from(u32::from_be(decode.sin_addr.s_addr)),
72        u16::from_be(decode.sin_port),
73    ))
74}
75
76#[inline]
77pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result<SocketAddrV6, Errno> {
78    if addr.address_family() != AddressFamily::INET6 {
79        return Err(Errno::AFNOSUPPORT);
80    }
81    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in6>());
82    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in6>() };
83    Ok(SocketAddrV6::new(
84        Ipv6Addr::from(unsafe { decode.sin6_addr.in6_u.u6_addr8 }),
85        u16::from_be(decode.sin6_port),
86        u32::from_be(decode.sin6_flowinfo),
87        decode.sin6_scope_id,
88    ))
89}
90
91#[inline]
92pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result<SocketAddrUnix, Errno> {
93    if addr.address_family() != AddressFamily::UNIX {
94        return Err(Errno::AFNOSUPPORT);
95    }
96    let offsetof_sun_path = super::addr::offsetof_sun_path();
97    let len = addr.addr_len() as usize;
98
99    assert!(len >= offsetof_sun_path);
100
101    if len == offsetof_sun_path {
102        SocketAddrUnix::new(&[][..])
103    } else {
104        let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_un>() };
105
106        // On Linux check for Linux's [abstract namespace].
107        //
108        // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
109        if decode.sun_path[0] == 0 {
110            let bytes = &decode.sun_path[1..len - offsetof_sun_path];
111
112            // SAFETY: Convert `&[c_char]` to `&[u8]`.
113            let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<u8>(), bytes.len()) };
114
115            return SocketAddrUnix::new_abstract_name(bytes);
116        }
117
118        // Otherwise we expect a NUL-terminated filesystem path.
119        let bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
120
121        // SAFETY: Convert `&[c_char]` to `&[u8]`.
122        let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<u8>(), bytes.len()) };
123
124        assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
125        SocketAddrUnix::new(bytes)
126    }
127}
128
129#[inline]
130pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result<SocketAddrXdp, Errno> {
131    if addr.address_family() != AddressFamily::XDP {
132        return Err(Errno::AFNOSUPPORT);
133    }
134    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_xdp>());
135    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_xdp>() };
136
137    // This ignores the `sxdp_shared_umem_fd` field, which is only expected to
138    // be significant in `bind` calls, and not returned from `acceptfrom` or
139    // `recvmsg` or similar.
140    Ok(SocketAddrXdp::new(
141        SocketAddrXdpFlags::from_bits_retain(decode.sxdp_flags),
142        u32::from_be(decode.sxdp_ifindex),
143        u32::from_be(decode.sxdp_queue_id),
144    ))
145}
146
147#[inline]
148pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result<SocketAddrNetlink, Errno> {
149    if addr.address_family() != AddressFamily::NETLINK {
150        return Err(Errno::AFNOSUPPORT);
151    }
152    assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_nl>());
153    let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_nl>() };
154    Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups))
155}