rustix/backend/linux_raw/net/
addr.rs

1//! Socket address utilities.
2//!
3//! # Safety
4//!
5//! This file uses `CStr::from_bytes_with_nul_unchecked` on a string it knows
6//! to be NUL-terminated.
7#![allow(unsafe_code)]
8
9use crate::backend::c;
10use crate::ffi::CStr;
11use crate::net::addr::SocketAddrLen;
12use crate::net::AddressFamily;
13use crate::{io, path};
14use core::cmp::Ordering;
15use core::hash::{Hash, Hasher};
16use core::{fmt, slice};
17#[cfg(feature = "alloc")]
18use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec};
19
20/// `struct sockaddr_un`
21#[derive(Clone)]
22#[doc(alias = "sockaddr_un")]
23pub struct SocketAddrUnix {
24    pub(crate) unix: c::sockaddr_un,
25    len: c::socklen_t,
26}
27
28impl SocketAddrUnix {
29    /// Construct a new Unix-domain address from a filesystem path.
30    #[inline]
31    pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
32        path.into_with_c_str(Self::_new)
33    }
34
35    #[inline]
36    fn _new(path: &CStr) -> io::Result<Self> {
37        let mut unix = Self::init();
38        let mut bytes = path.to_bytes_with_nul();
39        if bytes.len() > unix.sun_path.len() {
40            bytes = path.to_bytes(); // without NUL
41            if bytes.len() > unix.sun_path.len() {
42                return Err(io::Errno::NAMETOOLONG);
43            }
44        }
45        for (i, b) in bytes.iter().enumerate() {
46            unix.sun_path[i] = bitcast!(*b);
47        }
48        let len = offsetof_sun_path() + bytes.len();
49        let len = len.try_into().unwrap();
50        Ok(Self { unix, len })
51    }
52
53    /// Construct a new abstract Unix-domain address from a byte slice.
54    #[inline]
55    pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
56        let mut unix = Self::init();
57        let id = &mut unix.sun_path[1..];
58
59        // SAFETY: Convert `&mut [c_char]` to `&mut [u8]`.
60        let id = unsafe { slice::from_raw_parts_mut(id.as_mut_ptr().cast::<u8>(), id.len()) };
61
62        if let Some(id) = id.get_mut(..name.len()) {
63            id.copy_from_slice(name);
64            let len = offsetof_sun_path() + 1 + name.len();
65            let len = len.try_into().unwrap();
66            Ok(Self { unix, len })
67        } else {
68            Err(io::Errno::NAMETOOLONG)
69        }
70    }
71
72    /// Construct a new unnamed address.
73    ///
74    /// The kernel will assign an abstract Unix-domain address to the socket
75    /// when you call [`bind`][crate::net::bind]. You can inspect the assigned
76    /// name with [`getsockname`][crate::net::getsockname].
77    ///
78    /// # References
79    ///  - [Linux]
80    ///
81    /// [Linux]: https://www.man7.org/linux/man-pages/man7/unix.7.html
82    #[inline]
83    pub fn new_unnamed() -> Self {
84        Self {
85            unix: Self::init(),
86            len: offsetof_sun_path() as SocketAddrLen,
87        }
88    }
89
90    const fn init() -> c::sockaddr_un {
91        c::sockaddr_un {
92            sun_family: c::AF_UNIX as _,
93            sun_path: [0; 108],
94        }
95    }
96
97    /// For a filesystem path address, return the path.
98    #[inline]
99    #[cfg(feature = "alloc")]
100    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
101    pub fn path(&self) -> Option<Cow<'_, CStr>> {
102        let bytes = self.bytes()?;
103        if !bytes.is_empty() && bytes[0] != 0 {
104            if self.unix.sun_path.len() == bytes.len() {
105                // SAFETY: There are no NULs contained in bytes.
106                unsafe { Self::path_with_termination(bytes) }
107            } else {
108                // SAFETY: `from_bytes_with_nul_unchecked` since the string is
109                // NUL-terminated.
110                Some(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }.into())
111            }
112        } else {
113            None
114        }
115    }
116
117    /// If the `sun_path` field is not NUL-terminated, terminate it.
118    ///
119    /// SAFETY: The input `bytes` must not contain any NULs.
120    #[cfg(feature = "alloc")]
121    #[cold]
122    unsafe fn path_with_termination(bytes: &[u8]) -> Option<Cow<'_, CStr>> {
123        let mut owned = Vec::with_capacity(bytes.len() + 1);
124        owned.extend_from_slice(bytes);
125        owned.push(b'\0');
126        // SAFETY: `from_vec_with_nul_unchecked` since the string is
127        // NUL-terminated and `bytes` does not contain any NULs.
128        Some(Cow::Owned(
129            CString::from_vec_with_nul_unchecked(owned).into(),
130        ))
131    }
132
133    /// For a filesystem path address, return the path as a byte sequence,
134    /// excluding the NUL terminator.
135    #[inline]
136    pub fn path_bytes(&self) -> Option<&[u8]> {
137        let bytes = self.bytes()?;
138        if !bytes.is_empty() && bytes[0] != 0 {
139            if self.unix.sun_path.len() == self.len() - offsetof_sun_path() {
140                // There is no NUL terminator.
141                Some(bytes)
142            } else {
143                // Remove the NUL terminator.
144                Some(&bytes[..bytes.len() - 1])
145            }
146        } else {
147            None
148        }
149    }
150
151    /// For an abstract address, return the identifier.
152    #[inline]
153    pub fn abstract_name(&self) -> Option<&[u8]> {
154        if let [0, bytes @ ..] = self.bytes()? {
155            Some(bytes)
156        } else {
157            None
158        }
159    }
160
161    /// `true` if the socket address is unnamed.
162    #[inline]
163    pub fn is_unnamed(&self) -> bool {
164        self.bytes() == Some(&[])
165    }
166
167    #[inline]
168    pub(crate) fn addr_len(&self) -> SocketAddrLen {
169        bitcast!(self.len)
170    }
171
172    #[inline]
173    pub(crate) fn len(&self) -> usize {
174        self.addr_len() as usize
175    }
176
177    #[inline]
178    fn bytes(&self) -> Option<&[u8]> {
179        let len = self.len();
180        if len != 0 {
181            let bytes = &self.unix.sun_path[..len - offsetof_sun_path()];
182            // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
183            Some(unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) })
184        } else {
185            None
186        }
187    }
188}
189
190impl PartialEq for SocketAddrUnix {
191    #[inline]
192    fn eq(&self, other: &Self) -> bool {
193        let self_len = self.len() - offsetof_sun_path();
194        let other_len = other.len() - offsetof_sun_path();
195        self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
196    }
197}
198
199impl Eq for SocketAddrUnix {}
200
201impl PartialOrd for SocketAddrUnix {
202    #[inline]
203    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
204        Some(self.cmp(other))
205    }
206}
207
208impl Ord for SocketAddrUnix {
209    #[inline]
210    fn cmp(&self, other: &Self) -> Ordering {
211        let self_len = self.len() - offsetof_sun_path();
212        let other_len = other.len() - offsetof_sun_path();
213        self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
214    }
215}
216
217impl Hash for SocketAddrUnix {
218    #[inline]
219    fn hash<H: Hasher>(&self, state: &mut H) {
220        let self_len = self.len() - offsetof_sun_path();
221        self.unix.sun_path[..self_len].hash(state)
222    }
223}
224
225impl fmt::Debug for SocketAddrUnix {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        #[cfg(feature = "alloc")]
228        if let Some(path) = self.path() {
229            return path.fmt(f);
230        }
231        if let Some(bytes) = self.path_bytes() {
232            if let Ok(s) = core::str::from_utf8(bytes) {
233                return s.fmt(f);
234            }
235            return bytes.fmt(f);
236        }
237        if let Some(name) = self.abstract_name() {
238            return name.fmt(f);
239        }
240        "(unnamed)".fmt(f)
241    }
242}
243
244/// `struct sockaddr_storage`
245///
246/// This type is guaranteed to be large enough to hold any encoded socket
247/// address.
248#[repr(transparent)]
249#[derive(Copy, Clone)]
250#[doc(alias = "sockaddr_storage")]
251pub struct SocketAddrStorage(c::sockaddr_storage);
252
253// SAFETY: Bindgen adds a union with a raw pointer for alignment but it's never
254// used. `sockaddr_storage` is just a bunch of bytes and it doesn't hold
255// pointers.
256unsafe impl Send for SocketAddrStorage {}
257
258// SAFETY: Same as with `Send`.
259unsafe impl Sync for SocketAddrStorage {}
260
261impl SocketAddrStorage {
262    /// Return a socket addr storage initialized to all zero bytes. The
263    /// `sa_family` is set to [`AddressFamily::UNSPEC`].
264    pub fn zeroed() -> Self {
265        assert_eq!(c::AF_UNSPEC, 0);
266        // SAFETY: `sockaddr_storage` is meant to be zero-initializable.
267        unsafe { core::mem::zeroed() }
268    }
269
270    /// Return the `sa_family` of this socket address.
271    pub fn family(&self) -> AddressFamily {
272        // SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
273        unsafe {
274            AddressFamily::from_raw(crate::backend::net::read_sockaddr::read_sa_family(
275                crate::utils::as_ptr(&self.0).cast::<c::sockaddr>(),
276            ))
277        }
278    }
279
280    /// Clear the `sa_family` of this socket address to
281    /// [`AddressFamily::UNSPEC`].
282    pub fn clear_family(&mut self) {
283        // SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
284        unsafe {
285            crate::backend::net::read_sockaddr::initialize_family_to_unspec(
286                crate::utils::as_mut_ptr(&mut self.0).cast::<c::sockaddr>(),
287            )
288        }
289    }
290}
291
292/// Return the offset of the `sun_path` field of `sockaddr_un`.
293#[inline]
294pub(crate) fn offsetof_sun_path() -> usize {
295    let z = c::sockaddr_un {
296        sun_family: 0_u16,
297        sun_path: [0; 108],
298    };
299    (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
300}