Skip to main content

std/os/unix/net/
addr.rs

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