monoio/net/unix/
socket_addr.rs

1//! SocketAddr for UDS.
2//! Forked from mio.
3
4use std::{
5    ascii,
6    cmp::Ordering,
7    ffi::OsStr,
8    fmt, io, mem,
9    os::unix::prelude::{FromRawFd, OsStrExt, RawFd},
10    path::Path,
11};
12
13use super::path_offset;
14
15/// Unix SocketAddr.
16/// There is no way to create a [`net::SocketAddr`] so we forked it from mio.
17#[derive(Clone)]
18pub struct SocketAddr {
19    sockaddr: libc::sockaddr_un,
20    socklen: libc::socklen_t,
21}
22
23struct AsciiEscaped<'a>(&'a [u8]);
24
25enum AddressKind<'a> {
26    Unnamed,
27    Pathname(&'a Path),
28    Abstract(&'a [u8]),
29}
30
31impl SocketAddr {
32    fn address(&self) -> AddressKind<'_> {
33        let offset = path_offset(&self.sockaddr);
34        // Don't underflow in `len` below.
35        if (self.socklen as usize) < offset {
36            return AddressKind::Unnamed;
37        }
38        let len = self.socklen as usize - offset;
39        let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
40
41        // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
42        if len == 0 || (cfg!(not(any(target_os = "linux", target_os = "android"))) && path[0] == 0)
43        {
44            AddressKind::Unnamed
45        } else if self.sockaddr.sun_path[0] == 0 {
46            AddressKind::Abstract(&path[1..len])
47        } else {
48            AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
49        }
50    }
51
52    #[allow(unused)]
53    pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
54    where
55        F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
56    {
57        let mut sockaddr = {
58            let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
59            unsafe { sockaddr.assume_init() }
60        };
61
62        let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
63        let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
64
65        f(raw_sockaddr, &mut socklen)?;
66        Ok(SocketAddr::from_parts(sockaddr, socklen))
67    }
68
69    pub(crate) fn from_parts(
70        sockaddr: libc::sockaddr_un,
71        mut socklen: libc::socklen_t,
72    ) -> SocketAddr {
73        fn sun_path_offset(addr: &libc::sockaddr_un) -> usize {
74            let base: usize = (addr as *const libc::sockaddr_un).cast::<()>() as usize;
75            let path: usize = (&addr.sun_path as *const libc::c_char).cast::<()>() as usize;
76            path - base
77        }
78
79        if socklen == 0 {
80            // When there is a datagram from unnamed unix socket
81            // linux returns zero bytes of address
82            socklen = sun_path_offset(&sockaddr) as libc::socklen_t; // i.e., zero-length address
83        } else if sockaddr.sun_family != libc::AF_UNIX as libc::sa_family_t {
84            panic!("file descriptor did not correspond to a Unix socket");
85        }
86
87        SocketAddr { sockaddr, socklen }
88    }
89
90    pub(crate) fn into_parts(self) -> (libc::sockaddr_un, libc::socklen_t) {
91        (self.sockaddr, self.socklen)
92    }
93
94    /// Returns `true` if the address is unnamed.
95    ///
96    /// Documentation reflected in [`SocketAddr`]
97    ///
98    /// [`SocketAddr`]: std::os::unix::net::SocketAddr
99    #[inline]
100    pub fn is_unnamed(&self) -> bool {
101        matches!(self.address(), AddressKind::Unnamed)
102    }
103
104    /// Returns the contents of this address if it is a `pathname` address.
105    ///
106    /// Documentation reflected in [`SocketAddr`]
107    ///
108    /// [`SocketAddr`]: std::os::unix::net::SocketAddr
109    #[inline]
110    pub fn as_pathname(&self) -> Option<&Path> {
111        if let AddressKind::Pathname(path) = self.address() {
112            Some(path)
113        } else {
114            None
115        }
116    }
117
118    /// Returns the contents of this address if it is an abstract namespace
119    /// without the leading null byte.
120    // Link to std::os::unix::net::SocketAddr pending
121    // https://github.com/rust-lang/rust/issues/85410.
122    #[inline]
123    pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
124        if let AddressKind::Abstract(path) = self.address() {
125            Some(path)
126        } else {
127            None
128        }
129    }
130
131    #[inline]
132    pub(crate) fn as_ptr(&self) -> *const libc::sockaddr_un {
133        &self.sockaddr as *const _
134    }
135
136    #[inline]
137    pub(crate) fn len(&self) -> libc::socklen_t {
138        self.socklen
139    }
140}
141
142impl fmt::Debug for SocketAddr {
143    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
144        match self.address() {
145            AddressKind::Unnamed => write!(fmt, "(unnamed)"),
146            AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
147            AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
148        }
149    }
150}
151
152impl fmt::Display for AsciiEscaped<'_> {
153    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
154        write!(fmt, "\"")?;
155        for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
156            write!(fmt, "{}", byte as char)?;
157        }
158        write!(fmt, "\"")
159    }
160}
161
162pub(crate) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
163    let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
164
165    // This is safe to assume because a `libc::sockaddr_un` filled with `0`
166    // bytes is properly initialized.
167    //
168    // `0` is a valid value for `sockaddr_un::sun_family`; it is
169    // `libc::AF_UNSPEC`.
170    //
171    // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
172    // abstract path.
173    let mut sockaddr = unsafe { sockaddr.assume_init() };
174
175    sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
176
177    let bytes = path.as_os_str().as_bytes();
178    match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) {
179        // Abstract paths don't need a null terminator
180        (Some(&0), Ordering::Greater) => {
181            return Err(io::Error::new(
182                io::ErrorKind::InvalidInput,
183                "path must be no longer than libc::sockaddr_un.sun_path",
184            ));
185        }
186        (_, Ordering::Greater) | (_, Ordering::Equal) => {
187            return Err(io::Error::new(
188                io::ErrorKind::InvalidInput,
189                "path must be shorter than libc::sockaddr_un.sun_path",
190            ));
191        }
192        _ => {}
193    }
194
195    for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
196        *dst = *src as libc::c_char;
197    }
198
199    let offset = path_offset(&sockaddr);
200    let mut socklen = offset + bytes.len();
201
202    match bytes.first() {
203        // The struct has already been zeroes so the null byte for pathname
204        // addresses is already there.
205        Some(&0) | None => {}
206        Some(_) => socklen += 1,
207    }
208
209    Ok((sockaddr, socklen as libc::socklen_t))
210}
211
212pub(crate) fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
213where
214    T: FromRawFd,
215{
216    #[cfg(any(
217        target_os = "android",
218        target_os = "dragonfly",
219        target_os = "freebsd",
220        target_os = "illumos",
221        target_os = "netbsd",
222        target_os = "openbsd"
223    ))]
224    let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
225
226    #[cfg(target_os = "linux")]
227    let flags = {
228        if crate::driver::op::is_legacy() {
229            flags | libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK
230        } else {
231            flags | libc::SOCK_CLOEXEC
232        }
233    };
234
235    let mut fds = [-1; 2];
236    crate::syscall!(socketpair@RAW(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
237    let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
238    Ok(pair)
239}
240
241pub(crate) fn local_addr(socket: RawFd) -> io::Result<SocketAddr> {
242    SocketAddr::new(|sockaddr, socklen| crate::syscall!(getsockname@RAW(socket, sockaddr, socklen)))
243}
244
245pub(crate) fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> {
246    SocketAddr::new(|sockaddr, socklen| crate::syscall!(getpeername@RAW(socket, sockaddr, socklen)))
247}