mio/sys/unix/uds/
socketaddr.rs

1use super::path_offset;
2use std::ffi::OsStr;
3use std::os::unix::ffi::OsStrExt;
4use std::path::Path;
5use std::{ascii, fmt};
6
7/// An address associated with a `mio` specific Unix socket.
8///
9/// This is implemented instead of imported from [`net::SocketAddr`] because
10/// there is no way to create a [`net::SocketAddr`]. One must be returned by
11/// [`accept`], so this is returned instead.
12///
13/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr
14/// [`accept`]: #method.accept
15pub struct SocketAddr {
16    sockaddr: libc::sockaddr_un,
17    socklen: libc::socklen_t,
18}
19
20struct AsciiEscaped<'a>(&'a [u8]);
21
22enum AddressKind<'a> {
23    Unnamed,
24    Pathname(&'a Path),
25    Abstract(&'a [u8]),
26}
27
28impl SocketAddr {
29    fn address(&self) -> AddressKind<'_> {
30        let offset = path_offset(&self.sockaddr);
31        // Don't underflow in `len` below.
32        if (self.socklen as usize) < offset {
33            return AddressKind::Unnamed;
34        }
35        let len = self.socklen as usize - offset;
36        let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
37
38        // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
39        if len == 0
40            || (cfg!(not(any(target_os = "linux", target_os = "android")))
41                && self.sockaddr.sun_path[0] == 0)
42        {
43            AddressKind::Unnamed
44        } else if self.sockaddr.sun_path[0] == 0 {
45            AddressKind::Abstract(&path[1..len])
46        } else {
47            AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
48        }
49    }
50}
51
52cfg_os_poll! {
53    use std::{io, mem};
54
55    impl SocketAddr {
56        pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
57        where
58            F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
59        {
60            let mut sockaddr = {
61                let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
62                unsafe { sockaddr.assume_init() }
63            };
64
65            let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
66            let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
67
68            f(raw_sockaddr, &mut socklen)?;
69            Ok(SocketAddr::from_parts(sockaddr, socklen))
70        }
71
72        pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
73            SocketAddr { sockaddr, socklen }
74        }
75
76        pub(crate) fn raw_sockaddr(&self) -> &libc::sockaddr_un {
77            &self.sockaddr
78        }
79
80        pub(crate) fn raw_socklen(&self) -> &libc::socklen_t {
81            &self.socklen
82        }
83
84        /// Returns `true` if the address is unnamed.
85        ///
86        /// Documentation reflected in [`SocketAddr`]
87        ///
88        /// [`SocketAddr`]: std::os::unix::net::SocketAddr
89        pub fn is_unnamed(&self) -> bool {
90            matches!(self.address(), AddressKind::Unnamed)
91        }
92
93        /// Returns the contents of this address if it is a `pathname` address.
94        ///
95        /// Documentation reflected in [`SocketAddr`]
96        ///
97        /// [`SocketAddr`]: std::os::unix::net::SocketAddr
98        pub fn as_pathname(&self) -> Option<&Path> {
99            if let AddressKind::Pathname(path) = self.address() {
100                Some(path)
101            } else {
102                None
103            }
104        }
105
106        /// Returns the contents of this address if it is an abstract namespace
107        /// without the leading null byte.
108        // Link to std::os::unix::net::SocketAddr pending
109        // https://github.com/rust-lang/rust/issues/85410.
110        pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
111            if let AddressKind::Abstract(path) = self.address() {
112                Some(path)
113            } else {
114                None
115            }
116        }
117    }
118}
119
120impl fmt::Debug for SocketAddr {
121    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self.address() {
123            AddressKind::Unnamed => write!(fmt, "(unnamed)"),
124            AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
125            AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
126        }
127    }
128}
129
130impl<'a> fmt::Display for AsciiEscaped<'a> {
131    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(fmt, "\"")?;
133        for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
134            write!(fmt, "{}", byte as char)?;
135        }
136        write!(fmt, "\"")
137    }
138}