rustix/backend/libc/net/
addr.rs

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