Skip to main content

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