diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/mio/src/sys/unix/uds | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/mio/src/sys/unix/uds')
-rw-r--r-- | third_party/rust/mio/src/sys/unix/uds/datagram.rs | 56 | ||||
-rw-r--r-- | third_party/rust/mio/src/sys/unix/uds/listener.rs | 94 | ||||
-rw-r--r-- | third_party/rust/mio/src/sys/unix/uds/mod.rs | 149 | ||||
-rw-r--r-- | third_party/rust/mio/src/sys/unix/uds/socketaddr.rs | 130 | ||||
-rw-r--r-- | third_party/rust/mio/src/sys/unix/uds/stream.rs | 39 |
5 files changed, 468 insertions, 0 deletions
diff --git a/third_party/rust/mio/src/sys/unix/uds/datagram.rs b/third_party/rust/mio/src/sys/unix/uds/datagram.rs new file mode 100644 index 0000000000..d3e5314fe3 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/datagram.rs @@ -0,0 +1,56 @@ +use super::{socket_addr, SocketAddr}; +use crate::sys::unix::net::new_socket; + +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; + +pub(crate) fn bind(path: &Path) -> io::Result<net::UnixDatagram> { + let fd = new_socket(libc::AF_UNIX, libc::SOCK_DGRAM)?; + // Ensure the fd is closed. + let socket = unsafe { net::UnixDatagram::from_raw_fd(fd) }; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const _; + syscall!(bind(fd, sockaddr, socklen))?; + Ok(socket) +} + +pub(crate) fn unbound() -> io::Result<net::UnixDatagram> { + new_socket(libc::AF_UNIX, libc::SOCK_DGRAM) + .map(|socket| unsafe { net::UnixDatagram::from_raw_fd(socket) }) +} + +pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> { + super::pair(libc::SOCK_DGRAM) +} + +pub(crate) fn local_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> { + super::local_addr(socket.as_raw_fd()) +} + +pub(crate) fn peer_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> { + super::peer_addr(socket.as_raw_fd()) +} + +pub(crate) fn recv_from( + socket: &net::UnixDatagram, + dst: &mut [u8], +) -> io::Result<(usize, SocketAddr)> { + let mut count = 0; + let socketaddr = SocketAddr::new(|sockaddr, socklen| { + syscall!(recvfrom( + socket.as_raw_fd(), + dst.as_mut_ptr() as *mut _, + dst.len(), + 0, + sockaddr, + socklen, + )) + .map(|c| { + count = c; + c as libc::c_int + }) + })?; + Ok((count as usize, socketaddr)) +} diff --git a/third_party/rust/mio/src/sys/unix/uds/listener.rs b/third_party/rust/mio/src/sys/unix/uds/listener.rs new file mode 100644 index 0000000000..b6218427f2 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/listener.rs @@ -0,0 +1,94 @@ +use super::socket_addr; +use crate::net::{SocketAddr, UnixStream}; +use crate::sys::unix::net::new_socket; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; +use std::{io, mem}; + +pub(crate) fn bind(path: &Path) -> io::Result<net::UnixListener> { + let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr; + + syscall!(bind(socket, sockaddr, socklen)) + .and_then(|_| syscall!(listen(socket, 1024))) + .map_err(|err| { + // Close the socket if we hit an error, ignoring the error from + // closing since we can't pass back two errors. + let _ = unsafe { libc::close(socket) }; + err + }) + .map(|_| unsafe { net::UnixListener::from_raw_fd(socket) }) +} + +pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + + // This is safe to assume because a `libc::sockaddr_un` filled with `0` + // bytes is properly initialized. + // + // `0` is a valid value for `sockaddr_un::sun_family`; it is + // `libc::AF_UNSPEC`. + // + // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an + // abstract path. + let mut sockaddr = unsafe { sockaddr.assume_init() }; + + sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; + let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t; + + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + // Android x86's seccomp profile forbids calls to `accept4(2)` + // See https://github.com/tokio-rs/mio/issues/1445 for details + all( + target_arch = "x86", + target_os = "android" + ) + )))] + let socket = { + let flags = libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; + syscall!(accept4( + listener.as_raw_fd(), + &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr, + &mut socklen, + flags + )) + .map(|socket| unsafe { net::UnixStream::from_raw_fd(socket) }) + }; + + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + all(target_arch = "x86", target_os = "android") + ))] + let socket = syscall!(accept( + listener.as_raw_fd(), + &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr, + &mut socklen, + )) + .and_then(|socket| { + // Ensure the socket is closed if either of the `fcntl` calls + // error below. + let s = unsafe { net::UnixStream::from_raw_fd(socket) }; + syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?; + + // See https://github.com/tokio-rs/mio/issues/1450 + #[cfg(all(target_arch = "x86", target_os = "android"))] + syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?; + + Ok(s) + }); + + socket + .map(UnixStream::from_std) + .map(|stream| (stream, SocketAddr::from_parts(sockaddr, socklen))) +} + +pub(crate) fn local_addr(listener: &net::UnixListener) -> io::Result<SocketAddr> { + super::local_addr(listener.as_raw_fd()) +} diff --git a/third_party/rust/mio/src/sys/unix/uds/mod.rs b/third_party/rust/mio/src/sys/unix/uds/mod.rs new file mode 100644 index 0000000000..8e28a9573a --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/mod.rs @@ -0,0 +1,149 @@ +mod socketaddr; +pub use self::socketaddr::SocketAddr; + +/// Get the `sun_path` field offset of `sockaddr_un` for the target OS. +/// +/// On Linux, this funtion equates to the same value as +/// `size_of::<sa_family_t>()`, but some other implementations include +/// other fields before `sun_path`, so the expression more portably +/// describes the size of the address structure. +pub(in crate::sys) fn path_offset(sockaddr: &libc::sockaddr_un) -> usize { + let base = sockaddr as *const _ as usize; + let path = &sockaddr.sun_path as *const _ as usize; + path - base +} + +cfg_os_poll! { + use std::cmp::Ordering; + use std::os::unix::ffi::OsStrExt; + use std::os::unix::io::{RawFd, FromRawFd}; + use std::path::Path; + use std::{io, mem}; + + pub(crate) mod datagram; + pub(crate) mod listener; + pub(crate) mod stream; + + pub(in crate::sys) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + + // This is safe to assume because a `libc::sockaddr_un` filled with `0` + // bytes is properly initialized. + // + // `0` is a valid value for `sockaddr_un::sun_family`; it is + // `libc::AF_UNSPEC`. + // + // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an + // abstract path. + let mut sockaddr = unsafe { sockaddr.assume_init() }; + + sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + let bytes = path.as_os_str().as_bytes(); + match (bytes.get(0), bytes.len().cmp(&sockaddr.sun_path.len())) { + // Abstract paths don't need a null terminator + (Some(&0), Ordering::Greater) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be no longer than libc::sockaddr_un.sun_path", + )); + } + (_, Ordering::Greater) | (_, Ordering::Equal) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be shorter than libc::sockaddr_un.sun_path", + )); + } + _ => {} + } + + for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) { + *dst = *src as libc::c_char; + } + + let offset = path_offset(&sockaddr); + let mut socklen = offset + bytes.len(); + + match bytes.get(0) { + // The struct has already been zeroes so the null byte for pathname + // addresses is already there. + Some(&0) | None => {} + Some(_) => socklen += 1, + } + + Ok((sockaddr, socklen as libc::socklen_t)) + } + + fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)> + where T: FromRawFd, + { + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; + + let mut fds = [-1; 2]; + syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?; + let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) }; + + // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. + // + // In order to set those flags, additional `fcntl` sys calls must be + // performed. If a `fnctl` fails after the sockets have been created, + // the file descriptors will leak. Creating `pair` above ensures that if + // there is an error, the file descriptors are closed. + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; + syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; + syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; + syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; + } + Ok(pair) + } + + // The following functions can't simply be replaced with a call to + // `net::UnixDatagram` because of our `SocketAddr` type. + + fn local_addr(socket: RawFd) -> io::Result<SocketAddr> { + SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) + } + + fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> { + SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen))) + } + + #[cfg(test)] + mod tests { + use super::{path_offset, socket_addr}; + use std::path::Path; + use std::str; + + #[test] + fn pathname_address() { + const PATH: &str = "./foo/bar.txt"; + const PATH_LEN: usize = 13; + + // Pathname addresses do have a null terminator, so `socklen` is + // expected to be `PATH_LEN` + `offset` + 1. + let path = Path::new(PATH); + let (sockaddr, actual) = socket_addr(path).unwrap(); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset + 1; + assert_eq!(expected as libc::socklen_t, actual) + } + + #[test] + fn abstract_address() { + const PATH: &[u8] = &[0, 116, 111, 107, 105, 111]; + const PATH_LEN: usize = 6; + + // Abstract addresses do not have a null terminator, so `socklen` is + // expected to be `PATH_LEN` + `offset`. + let abstract_path = str::from_utf8(PATH).unwrap(); + let path = Path::new(abstract_path); + let (sockaddr, actual) = socket_addr(path).unwrap(); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset; + assert_eq!(expected as libc::socklen_t, actual) + } + } +} diff --git a/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs b/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs new file mode 100644 index 0000000000..4c7c411618 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs @@ -0,0 +1,130 @@ +use super::path_offset; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; +use std::path::Path; +use std::{ascii, fmt}; + +/// An address associated with a `mio` specific Unix socket. +/// +/// This is implemented instead of imported from [`net::SocketAddr`] because +/// there is no way to create a [`net::SocketAddr`]. One must be returned by +/// [`accept`], so this is returned instead. +/// +/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr +/// [`accept`]: #method.accept +pub struct SocketAddr { + sockaddr: libc::sockaddr_un, + socklen: libc::socklen_t, +} + +struct AsciiEscaped<'a>(&'a [u8]); + +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a [u8]), +} + +impl SocketAddr { + fn address(&self) -> AddressKind<'_> { + let offset = path_offset(&self.sockaddr); + // Don't underflow in `len` below. + if (self.socklen as usize) < offset { + return AddressKind::Unnamed; + } + let len = self.socklen as usize - offset; + let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) }; + + // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses + if len == 0 + || (cfg!(not(any(target_os = "linux", target_os = "android"))) + && self.sockaddr.sun_path[0] == 0) + { + AddressKind::Unnamed + } else if self.sockaddr.sun_path[0] == 0 { + AddressKind::Abstract(&path[1..len]) + } else { + AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) + } + } +} + +cfg_os_poll! { + use std::{io, mem}; + + impl SocketAddr { + pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr> + where + F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>, + { + let mut sockaddr = { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + unsafe { sockaddr.assume_init() } + }; + + let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr; + let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t; + + f(raw_sockaddr, &mut socklen)?; + Ok(SocketAddr::from_parts(sockaddr, socklen)) + } + + pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr { + SocketAddr { sockaddr, socklen } + } + + /// Returns `true` if the address is unnamed. + /// + /// Documentation reflected in [`SocketAddr`] + /// + /// [`SocketAddr`]: std::os::unix::net::SocketAddr + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// Documentation reflected in [`SocketAddr`] + /// + /// [`SocketAddr`]: std::os::unix::net::SocketAddr + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { + Some(path) + } else { + None + } + } + + /// Returns the contents of this address if it is an abstract namespace + /// without the leading null byte. + // Link to std::os::unix::net::SocketAddr pending + // https://github.com/rust-lang/rust/issues/85410. + pub fn as_abstract_namespace(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(path) = self.address() { + Some(path) + } else { + None + } + } + } +} + +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), + AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), + } + } +} + +impl<'a> fmt::Display for AsciiEscaped<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"")?; + for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { + write!(fmt, "{}", byte as char)?; + } + write!(fmt, "\"") + } +} diff --git a/third_party/rust/mio/src/sys/unix/uds/stream.rs b/third_party/rust/mio/src/sys/unix/uds/stream.rs new file mode 100644 index 0000000000..149dd14e1d --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/stream.rs @@ -0,0 +1,39 @@ +use super::{socket_addr, SocketAddr}; +use crate::sys::unix::net::new_socket; + +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; + +pub(crate) fn connect(path: &Path) -> io::Result<net::UnixStream> { + let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr; + + match syscall!(connect(socket, sockaddr, socklen)) { + Ok(_) => {} + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} + Err(e) => { + // Close the socket if we hit an error, ignoring the error + // from closing since we can't pass back two errors. + let _ = unsafe { libc::close(socket) }; + + return Err(e); + } + } + + Ok(unsafe { net::UnixStream::from_raw_fd(socket) }) +} + +pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> { + super::pair(libc::SOCK_STREAM) +} + +pub(crate) fn local_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> { + super::local_addr(socket.as_raw_fd()) +} + +pub(crate) fn peer_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> { + super::peer_addr(socket.as_raw_fd()) +} |