mio/sys/unix/uds/
listener.rs

1use std::ffi::OsStr;
2use std::os::fd::{AsRawFd, FromRawFd};
3use std::os::unix::ffi::OsStrExt;
4use std::os::unix::net::{self, SocketAddr};
5use std::path::Path;
6use std::{io, mem};
7
8use crate::net::UnixStream;
9use crate::sys::unix::net::new_socket;
10use crate::sys::unix::uds::{path_offset, unix_addr};
11
12pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result<net::UnixListener> {
13    let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
14    let socket = unsafe { net::UnixListener::from_raw_fd(fd) };
15
16    let (unix_address, addrlen) = unix_addr(address);
17    let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
18    syscall!(bind(fd, sockaddr, addrlen))?;
19    syscall!(listen(fd, 1024))?;
20
21    Ok(socket)
22}
23
24pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
25    // SAFETY: `libc::sockaddr_un` zero filled is properly initialized.
26    //
27    // `0` is a valid value for `sockaddr_un::sun_family`; it is
28    // `libc::AF_UNSPEC`.
29    //
30    // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
31    // abstract path.
32    let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
33
34    let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
35
36    #[cfg(not(any(
37        target_os = "aix",
38        target_os = "haiku",
39        target_os = "ios",
40        target_os = "macos",
41        target_os = "netbsd",
42        target_os = "redox",
43        target_os = "tvos",
44        target_os = "visionos",
45        target_os = "watchos",
46        target_os = "espidf",
47        target_os = "vita",
48        target_os = "nto",
49        // Android x86's seccomp profile forbids calls to `accept4(2)`
50        // See https://github.com/tokio-rs/mio/issues/1445 for details
51        all(target_arch = "x86", target_os = "android"),
52    )))]
53    let socket = {
54        let flags = libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
55        syscall!(accept4(
56            listener.as_raw_fd(),
57            &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
58            &mut socklen,
59            flags
60        ))
61        .map(|socket| unsafe { net::UnixStream::from_raw_fd(socket) })
62    };
63
64    #[cfg(any(
65        target_os = "aix",
66        target_os = "haiku",
67        target_os = "ios",
68        target_os = "macos",
69        target_os = "netbsd",
70        target_os = "redox",
71        target_os = "tvos",
72        target_os = "visionos",
73        target_os = "watchos",
74        target_os = "espidf",
75        target_os = "vita",
76        target_os = "nto",
77        all(target_arch = "x86", target_os = "android")
78    ))]
79    let socket = syscall!(accept(
80        listener.as_raw_fd(),
81        &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
82        &mut socklen,
83    ))
84    .and_then(|socket| {
85        // Ensure the socket is closed if either of the `fcntl` calls
86        // error below.
87        let s = unsafe { net::UnixStream::from_raw_fd(socket) };
88        #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
89        syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?;
90
91        // See https://github.com/tokio-rs/mio/issues/1450
92        #[cfg(any(
93            all(target_arch = "x86", target_os = "android"),
94            target_os = "espidf",
95            target_os = "vita",
96            target_os = "nto",
97        ))]
98        syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?;
99
100        Ok(s)
101    });
102
103    let socket = socket.map(UnixStream::from_std)?;
104
105    #[allow(unused_mut)] // See below.
106    let mut path_len = socklen as usize - path_offset(&sockaddr);
107    // On FreeBSD and Darwin, it returns a length of 14/16, but an unnamed (all
108    // zero) address. Map that to a length of 0 to match other OS.
109    if sockaddr.sun_path[0] == 0 {
110        path_len = 0;
111    }
112    // SAFETY: going from i8 to u8 is fine in this context.
113    let mut path =
114        unsafe { &*(&sockaddr.sun_path[..path_len] as *const [libc::c_char] as *const [u8]) };
115    // Remove last null as `SocketAddr::from_pathname` doesn't accept it.
116    if let Some(0) = path.last() {
117        path = &path[..path.len() - 1];
118    }
119    let address = SocketAddr::from_pathname(Path::new(OsStr::from_bytes(path)))?;
120    Ok((socket, address))
121}