mio/sys/unix/uds/
mod.rs

1#[cfg(target_os = "android")]
2use std::os::android::net::SocketAddrExt;
3#[cfg(target_os = "linux")]
4use std::os::linux::net::SocketAddrExt;
5use std::os::unix::ffi::OsStrExt;
6use std::os::unix::io::FromRawFd;
7use std::os::unix::net::SocketAddr;
8use std::{io, mem, ptr};
9
10pub(crate) mod datagram;
11pub(crate) mod listener;
12pub(crate) mod stream;
13
14const UNNAMED_ADDRESS: &[u8] = &[];
15
16/// Get the `sun_path` field offset of `sockaddr_un` for the target OS.
17///
18/// On Linux, this function equates to the same value as
19/// `size_of::<sa_family_t>()`, but some other implementations include
20/// other fields before `sun_path`, so the expression more portably
21/// describes the size of the address structure.
22fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
23    let base = sockaddr as *const _ as usize;
24    let path = &sockaddr.sun_path as *const _ as usize;
25    path - base
26}
27
28/// Converts a Rust `SocketAddr` into the system representation.
29fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) {
30    // SAFETY: `libc::sockaddr_un` zero filled is properly initialized.
31    //
32    // `0` is a valid value for `sockaddr_un::sun_family`; it is
33    // `libc::AF_UNSPEC`.
34    //
35    // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
36    // abstract path.
37    let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
38
39    sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
40
41    #[allow(unused_mut)] // Only used with abstract namespaces.
42    let mut offset = 0;
43    let addr = match address.as_pathname() {
44        Some(path) => path.as_os_str().as_bytes(),
45        #[cfg(any(target_os = "android", target_os = "linux"))]
46        None => match address.as_abstract_name() {
47            Some(name) => {
48                offset += 1;
49                name
50            }
51            None => UNNAMED_ADDRESS,
52        },
53        #[cfg(not(any(target_os = "android", target_os = "linux")))]
54        None => UNNAMED_ADDRESS,
55    };
56
57    // SAFETY: `addr` and `sockaddr.sun_path` are not overlapping and both point
58    // to valid memory.
59    // SAFETY: since `addr` is a valid Unix address, it must not be larger than
60    // `SUN_LEN` bytes, thus we won't overwrite the size of sockaddr.sun_path.
61    // SAFETY: null byte is already written because we zeroed the address above.
62    debug_assert!(offset + addr.len() <= sockaddr.sun_path.len());
63    unsafe {
64        ptr::copy_nonoverlapping(
65            addr.as_ptr(),
66            sockaddr.sun_path.as_mut_ptr().add(offset).cast(),
67            addr.len(),
68        )
69    };
70
71    let mut addrlen = path_offset(&sockaddr) + addr.len();
72    // +1 for null byte at the end of the path, not needed for abstract
73    // namespaces (which start with a null byte).
74    match addr.first() {
75        Some(&0) | None => {}
76        Some(_) => addrlen += 1,
77    }
78
79    // SAFETY: the length is fine to cast to `socklen_t` as it's 32 bits and the
80    // address can be at most `SUN_LEN` bytes.
81    (sockaddr, addrlen as _)
82}
83
84fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
85where
86    T: FromRawFd,
87{
88    #[cfg(not(any(
89        target_os = "aix",
90        target_os = "haiku",
91        target_os = "ios",
92        target_os = "macos",
93        target_os = "nto",
94        target_os = "tvos",
95        target_os = "visionos",
96        target_os = "watchos",
97        target_os = "espidf",
98        target_os = "vita",
99    )))]
100    let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
101
102    let mut fds = [-1; 2];
103    syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
104    let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
105
106    // Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
107    //
108    // In order to set those flags, additional `fcntl` sys calls must be
109    // performed. If a `fnctl` fails after the sockets have been created,
110    // the file descriptors will leak. Creating `pair` above ensures that if
111    // there is an error, the file descriptors are closed.
112    #[cfg(any(
113        target_os = "aix",
114        target_os = "haiku",
115        target_os = "ios",
116        target_os = "macos",
117        target_os = "nto",
118        target_os = "tvos",
119        target_os = "visionos",
120        target_os = "watchos",
121        target_os = "espidf",
122        target_os = "vita",
123    ))]
124    {
125        syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
126        #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
127        syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
128        syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
129        #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
130        syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
131    }
132
133    Ok(pair)
134}
135
136#[cfg(test)]
137mod tests {
138    use std::os::unix::net::SocketAddr;
139    use std::path::Path;
140    use std::str;
141
142    use super::{path_offset, unix_addr};
143
144    #[test]
145    fn pathname_address() {
146        const PATH: &str = "./foo/bar.txt";
147        const PATH_LEN: usize = 13;
148
149        // Pathname addresses do have a null terminator, so `socklen` is
150        // expected to be `PATH_LEN` + `offset` + 1.
151        let address = SocketAddr::from_pathname(Path::new(PATH)).unwrap();
152        let (sockaddr, actual) = unix_addr(&address);
153        let offset = path_offset(&sockaddr);
154        let expected = PATH_LEN + offset + 1;
155        assert_eq!(expected as libc::socklen_t, actual)
156    }
157
158    #[test]
159    #[cfg(any(target_os = "android", target_os = "linux"))]
160    fn abstract_address() {
161        use std::os::linux::net::SocketAddrExt;
162
163        const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
164        const PATH_LEN: usize = 6;
165
166        // Abstract addresses do not have a null terminator, so `socklen` is
167        // expected to be `PATH_LEN` + `offset`.
168        let address = SocketAddr::from_abstract_name(PATH).unwrap();
169        let (sockaddr, actual) = unix_addr(&address);
170        let offset = path_offset(&sockaddr);
171        let expected = PATH_LEN + offset;
172        assert_eq!(expected as libc::socklen_t, actual)
173    }
174}