std/os/unix/net/
addr.rs

1use crate::ffi::OsStr;
2#[cfg(any(doc, target_os = "android", target_os = "linux"))]
3use crate::os::net::linux_ext;
4use crate::os::unix::ffi::OsStrExt;
5use crate::path::Path;
6use crate::sealed::Sealed;
7use crate::sys::cvt;
8use crate::{fmt, io, mem, ptr};
9
10// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
11#[cfg(not(unix))]
12#[allow(non_camel_case_types)]
13mod libc {
14    pub use core::ffi::c_int;
15    pub type socklen_t = u32;
16    pub struct sockaddr;
17    #[derive(Clone)]
18    pub struct sockaddr_un {
19        pub sun_path: [u8; 1],
20    }
21}
22
23const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path);
24
25pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
26    // SAFETY: All zeros is a valid representation for `sockaddr_un`.
27    let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };
28    addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
29
30    let bytes = path.as_os_str().as_bytes();
31
32    if bytes.contains(&0) {
33        return Err(io::const_error!(
34            io::ErrorKind::InvalidInput,
35            "paths must not contain interior null bytes",
36        ));
37    }
38
39    if bytes.len() >= addr.sun_path.len() {
40        return Err(io::const_error!(
41            io::ErrorKind::InvalidInput,
42            "path must be shorter than SUN_LEN",
43        ));
44    }
45    // SAFETY: `bytes` and `addr.sun_path` are not overlapping and
46    // both point to valid memory.
47    // NOTE: We zeroed the memory above, so the path is already null
48    // terminated.
49    unsafe {
50        ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len())
51    };
52
53    let mut len = SUN_PATH_OFFSET + bytes.len();
54    match bytes.get(0) {
55        Some(&0) | None => {}
56        Some(_) => len += 1,
57    }
58    Ok((addr, len as libc::socklen_t))
59}
60
61enum AddressKind<'a> {
62    Unnamed,
63    Pathname(&'a Path),
64    Abstract(&'a [u8]),
65}
66
67/// An address associated with a Unix socket.
68///
69/// # Examples
70///
71/// ```
72/// use std::os::unix::net::UnixListener;
73///
74/// let socket = match UnixListener::bind("/tmp/sock") {
75///     Ok(sock) => sock,
76///     Err(e) => {
77///         println!("Couldn't bind: {e:?}");
78///         return
79///     }
80/// };
81/// let addr = socket.local_addr().expect("Couldn't get local address");
82/// ```
83#[derive(Clone)]
84#[stable(feature = "unix_socket", since = "1.10.0")]
85pub struct SocketAddr {
86    pub(super) addr: libc::sockaddr_un,
87    pub(super) len: libc::socklen_t,
88}
89
90impl SocketAddr {
91    pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
92    where
93        F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
94    {
95        unsafe {
96            let mut addr: libc::sockaddr_un = mem::zeroed();
97            let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
98            cvt(f((&raw mut addr) as *mut _, &mut len))?;
99            SocketAddr::from_parts(addr, len)
100        }
101    }
102
103    pub(super) fn from_parts(
104        addr: libc::sockaddr_un,
105        mut len: libc::socklen_t,
106    ) -> io::Result<SocketAddr> {
107        if cfg!(target_os = "openbsd") {
108            // on OpenBSD, getsockname(2) returns the actual size of the socket address,
109            // and not the len of the content. Figure out the length for ourselves.
110            // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2
111            let sun_path: &[u8] =
112                unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) };
113            len = core::slice::memchr::memchr(0, sun_path)
114                .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t);
115        }
116
117        if len == 0 {
118            // When there is a datagram from unnamed unix socket
119            // linux returns zero bytes of address
120            len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address
121        } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
122            return Err(io::const_error!(
123                io::ErrorKind::InvalidInput,
124                "file descriptor did not correspond to a Unix socket",
125            ));
126        }
127
128        Ok(SocketAddr { addr, len })
129    }
130
131    /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
132    ///
133    /// # Errors
134    ///
135    /// Returns an error if the path is longer than `SUN_LEN` or if it contains
136    /// NULL bytes.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use std::os::unix::net::SocketAddr;
142    /// use std::path::Path;
143    ///
144    /// # fn main() -> std::io::Result<()> {
145    /// let address = SocketAddr::from_pathname("/path/to/socket")?;
146    /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket")));
147    /// # Ok(())
148    /// # }
149    /// ```
150    ///
151    /// Creating a `SocketAddr` with a NULL byte results in an error.
152    ///
153    /// ```
154    /// use std::os::unix::net::SocketAddr;
155    ///
156    /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err());
157    /// ```
158    #[stable(feature = "unix_socket_creation", since = "1.61.0")]
159    pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
160    where
161        P: AsRef<Path>,
162    {
163        sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len })
164    }
165
166    /// Returns `true` if the address is unnamed.
167    ///
168    /// # Examples
169    ///
170    /// A named address:
171    ///
172    /// ```no_run
173    /// use std::os::unix::net::UnixListener;
174    ///
175    /// fn main() -> std::io::Result<()> {
176    ///     let socket = UnixListener::bind("/tmp/sock")?;
177    ///     let addr = socket.local_addr().expect("Couldn't get local address");
178    ///     assert_eq!(addr.is_unnamed(), false);
179    ///     Ok(())
180    /// }
181    /// ```
182    ///
183    /// An unnamed address:
184    ///
185    /// ```
186    /// use std::os::unix::net::UnixDatagram;
187    ///
188    /// fn main() -> std::io::Result<()> {
189    ///     let socket = UnixDatagram::unbound()?;
190    ///     let addr = socket.local_addr().expect("Couldn't get local address");
191    ///     assert_eq!(addr.is_unnamed(), true);
192    ///     Ok(())
193    /// }
194    /// ```
195    #[must_use]
196    #[stable(feature = "unix_socket", since = "1.10.0")]
197    pub fn is_unnamed(&self) -> bool {
198        matches!(self.address(), AddressKind::Unnamed)
199    }
200
201    /// Returns the contents of this address if it is a `pathname` address.
202    ///
203    /// # Examples
204    ///
205    /// With a pathname:
206    ///
207    /// ```no_run
208    /// use std::os::unix::net::UnixListener;
209    /// use std::path::Path;
210    ///
211    /// fn main() -> std::io::Result<()> {
212    ///     let socket = UnixListener::bind("/tmp/sock")?;
213    ///     let addr = socket.local_addr().expect("Couldn't get local address");
214    ///     assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
215    ///     Ok(())
216    /// }
217    /// ```
218    ///
219    /// Without a pathname:
220    ///
221    /// ```
222    /// use std::os::unix::net::UnixDatagram;
223    ///
224    /// fn main() -> std::io::Result<()> {
225    ///     let socket = UnixDatagram::unbound()?;
226    ///     let addr = socket.local_addr().expect("Couldn't get local address");
227    ///     assert_eq!(addr.as_pathname(), None);
228    ///     Ok(())
229    /// }
230    /// ```
231    #[stable(feature = "unix_socket", since = "1.10.0")]
232    #[must_use]
233    pub fn as_pathname(&self) -> Option<&Path> {
234        if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
235    }
236
237    fn address(&self) -> AddressKind<'_> {
238        let len = self.len as usize - SUN_PATH_OFFSET;
239        let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
240
241        // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
242        if len == 0
243            || (cfg!(not(any(target_os = "linux", target_os = "android")))
244                && self.addr.sun_path[0] == 0)
245        {
246            AddressKind::Unnamed
247        } else if self.addr.sun_path[0] == 0 {
248            AddressKind::Abstract(&path[1..len])
249        } else {
250            AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
251        }
252    }
253}
254
255#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
256impl Sealed for SocketAddr {}
257
258#[doc(cfg(any(target_os = "android", target_os = "linux")))]
259#[cfg(any(doc, target_os = "android", target_os = "linux"))]
260#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
261impl linux_ext::addr::SocketAddrExt for SocketAddr {
262    fn as_abstract_name(&self) -> Option<&[u8]> {
263        if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
264    }
265
266    fn from_abstract_name<N>(name: N) -> crate::io::Result<Self>
267    where
268        N: AsRef<[u8]>,
269    {
270        let name = name.as_ref();
271        unsafe {
272            let mut addr: libc::sockaddr_un = mem::zeroed();
273            addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
274
275            if name.len() + 1 > addr.sun_path.len() {
276                return Err(io::const_error!(
277                    io::ErrorKind::InvalidInput,
278                    "abstract socket name must be shorter than SUN_LEN",
279                ));
280            }
281
282            crate::ptr::copy_nonoverlapping(
283                name.as_ptr(),
284                addr.sun_path.as_mut_ptr().add(1) as *mut u8,
285                name.len(),
286            );
287            let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t;
288            SocketAddr::from_parts(addr, len)
289        }
290    }
291}
292
293#[stable(feature = "unix_socket", since = "1.10.0")]
294impl fmt::Debug for SocketAddr {
295    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
296        match self.address() {
297            AddressKind::Unnamed => write!(fmt, "(unnamed)"),
298            AddressKind::Abstract(name) => write!(fmt, "\"{}\" (abstract)", name.escape_ascii()),
299            AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
300        }
301    }
302}