std/os/unix/net/
addr.rs

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