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    // Use the same backlog value as the standard libary.
20    // <https://github.com/rust-lang/rust/blob/0028f344ce9f64766259577c998a1959ca1f6a0b/library/std/src/os/unix/net/listener.rs#L75-L106>
21    let backlog = if cfg!(any(
22        target_os = "windows",
23        target_os = "redox",
24        target_os = "espidf",
25        target_os = "horizon"
26    )) {
27        128
28    } else if cfg!(any(
29        target_os = "linux",
30        target_os = "freebsd",
31        target_os = "openbsd",
32        target_vendor = "apple"
33    )) {
34        -1
35    } else {
36        libc::SOMAXCONN
37    };
38    syscall!(listen(fd, backlog))?;
39
40    Ok(socket)
41}
42
43pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
44    // SAFETY: `libc::sockaddr_un` zero filled is properly initialized.
45    //
46    // `0` is a valid value for `sockaddr_un::sun_family`; it is
47    // `libc::AF_UNSPEC`.
48    //
49    // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
50    // abstract path.
51    let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
52
53    let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
54
55    #[cfg(not(any(
56        target_os = "aix",
57        target_os = "haiku",
58        target_os = "ios",
59        target_os = "macos",
60        target_os = "netbsd",
61        target_os = "redox",
62        target_os = "tvos",
63        target_os = "visionos",
64        target_os = "watchos",
65        target_os = "espidf",
66        target_os = "vita",
67        target_os = "nto",
68        // Android x86's seccomp profile forbids calls to `accept4(2)`
69        // See https://github.com/tokio-rs/mio/issues/1445 for details
70        all(target_arch = "x86", target_os = "android"),
71    )))]
72    let socket = {
73        let flags = libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
74        syscall!(accept4(
75            listener.as_raw_fd(),
76            &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
77            &mut socklen,
78            flags
79        ))
80        .map(|socket| unsafe { net::UnixStream::from_raw_fd(socket) })
81    };
82
83    #[cfg(any(
84        target_os = "aix",
85        target_os = "haiku",
86        target_os = "ios",
87        target_os = "macos",
88        target_os = "netbsd",
89        target_os = "redox",
90        target_os = "tvos",
91        target_os = "visionos",
92        target_os = "watchos",
93        target_os = "espidf",
94        target_os = "vita",
95        target_os = "nto",
96        all(target_arch = "x86", target_os = "android")
97    ))]
98    let socket = syscall!(accept(
99        listener.as_raw_fd(),
100        &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
101        &mut socklen,
102    ))
103    .and_then(|socket| {
104        // Ensure the socket is closed if either of the `fcntl` calls
105        // error below.
106        let s = unsafe { net::UnixStream::from_raw_fd(socket) };
107        #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
108        syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?;
109
110        // See https://github.com/tokio-rs/mio/issues/1450
111        #[cfg(any(
112            all(target_arch = "x86", target_os = "android"),
113            target_os = "espidf",
114            target_os = "vita",
115            target_os = "nto",
116        ))]
117        syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?;
118
119        Ok(s)
120    });
121
122    let socket = socket.map(UnixStream::from_std)?;
123
124    #[allow(unused_mut)] // See below.
125    let mut path_len = socklen as usize - path_offset(&sockaddr);
126    // On FreeBSD and Darwin, it returns a length of 14/16, but an unnamed (all
127    // zero) address. Map that to a length of 0 to match other OS.
128    if sockaddr.sun_path[0] == 0 {
129        path_len = 0;
130    }
131    // SAFETY: going from i8 to u8 is fine in this context.
132    let mut path =
133        unsafe { &*(&sockaddr.sun_path[..path_len] as *const [libc::c_char] as *const [u8]) };
134    // Remove last null as `SocketAddr::from_pathname` doesn't accept it.
135    if let Some(0) = path.last() {
136        path = &path[..path.len() - 1];
137    }
138    let address = SocketAddr::from_pathname(Path::new(OsStr::from_bytes(path)))?;
139    Ok((socket, address))
140}