diff --git a/src/net/mod.rs b/src/net/mod.rs index 7d714ca00..15c405cf9 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -36,4 +36,4 @@ pub use self::udp::UdpSocket; #[cfg(unix)] mod uds; #[cfg(unix)] -pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream}; +pub use self::uds::{UnixDatagram, UnixListener, UnixStream}; diff --git a/src/net/uds/datagram.rs b/src/net/uds/datagram.rs index 4d8ff82f3..a5f427610 100644 --- a/src/net/uds/datagram.rs +++ b/src/net/uds/datagram.rs @@ -3,7 +3,7 @@ use crate::{event, sys, Interest, Registry, Token}; use std::net::Shutdown; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use std::os::unix::net; +use std::os::unix::net::{self, SocketAddr}; use std::path::Path; use std::{fmt, io}; @@ -15,7 +15,13 @@ pub struct UnixDatagram { impl UnixDatagram { /// Creates a Unix datagram socket bound to the given path. pub fn bind>(path: P) -> io::Result { - sys::uds::datagram::bind(path.as_ref()).map(UnixDatagram::from_std) + let addr = SocketAddr::from_pathname(path)?; + UnixDatagram::bind_addr(&addr) + } + + /// Creates a new `UnixDatagram` bound to the specified socket `address`. + pub fn bind_addr(address: &SocketAddr) -> io::Result { + sys::uds::datagram::bind_addr(address).map(UnixDatagram::from_std) } /// Creates a new `UnixDatagram` from a standard `net::UnixDatagram`. @@ -54,24 +60,23 @@ impl UnixDatagram { } /// Returns the address of this socket. - pub fn local_addr(&self) -> io::Result { - sys::uds::datagram::local_addr(&self.inner) + pub fn local_addr(&self) -> io::Result { + self.inner.local_addr() } /// Returns the address of this socket's peer. /// /// The `connect` method will connect the socket to a peer. - pub fn peer_addr(&self) -> io::Result { - sys::uds::datagram::peer_addr(&self.inner) + pub fn peer_addr(&self) -> io::Result { + self.inner.peer_addr() } /// Receives data from the socket. /// /// On success, returns the number of bytes read and the address from /// whence the data came. - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, sys::SocketAddr)> { - self.inner - .do_io(|inner| sys::uds::datagram::recv_from(inner, buf)) + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.do_io(|inner| inner.recv_from(buf)) } /// Receives data from the socket. diff --git a/src/net/uds/listener.rs b/src/net/uds/listener.rs index da1605375..94ed16c56 100644 --- a/src/net/uds/listener.rs +++ b/src/net/uds/listener.rs @@ -1,12 +1,12 @@ -use crate::io_source::IoSource; -use crate::net::{SocketAddr, UnixStream}; -use crate::{event, sys, Interest, Registry, Token}; - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use std::os::unix::net; +use std::os::unix::net::{self, SocketAddr}; use std::path::Path; use std::{fmt, io}; +use crate::io_source::IoSource; +use crate::net::UnixStream; +use crate::{event, sys, Interest, Registry, Token}; + /// A non-blocking Unix domain socket server. pub struct UnixListener { inner: IoSource, @@ -15,7 +15,8 @@ pub struct UnixListener { impl UnixListener { /// Creates a new `UnixListener` bound to the specified socket `path`. pub fn bind>(path: P) -> io::Result { - sys::uds::listener::bind(path.as_ref()).map(UnixListener::from_std) + let addr = SocketAddr::from_pathname(path)?; + UnixListener::bind_addr(&addr) } /// Creates a new `UnixListener` bound to the specified socket `address`. @@ -44,8 +45,8 @@ impl UnixListener { } /// Returns the local socket address of this listener. - pub fn local_addr(&self) -> io::Result { - sys::uds::listener::local_addr(&self.inner) + pub fn local_addr(&self) -> io::Result { + self.inner.local_addr() } /// Returns the value of the `SO_ERROR` option. diff --git a/src/net/uds/mod.rs b/src/net/uds/mod.rs index 6b4ffdc43..e02fd80dc 100644 --- a/src/net/uds/mod.rs +++ b/src/net/uds/mod.rs @@ -6,5 +6,3 @@ pub use self::listener::UnixListener; mod stream; pub use self::stream::UnixStream; - -pub use crate::sys::SocketAddr; diff --git a/src/net/uds/stream.rs b/src/net/uds/stream.rs index 70b5085b3..d3b910426 100644 --- a/src/net/uds/stream.rs +++ b/src/net/uds/stream.rs @@ -1,12 +1,11 @@ use crate::io_source::IoSource; -use crate::net::SocketAddr; use crate::{event, sys, Interest, Registry, Token}; use std::fmt; use std::io::{self, IoSlice, IoSliceMut, Read, Write}; use std::net::Shutdown; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use std::os::unix::net; +use std::os::unix::net::{self, SocketAddr}; use std::path::Path; /// A non-blocking Unix stream socket. @@ -20,7 +19,8 @@ impl UnixStream { /// This may return a `WouldBlock` in which case the socket connection /// cannot be completed immediately. Usually it means the backlog is full. pub fn connect>(path: P) -> io::Result { - sys::uds::stream::connect(path.as_ref()).map(UnixStream::from_std) + let addr = SocketAddr::from_pathname(path)?; + UnixStream::connect_addr(&addr) } /// Connects to the socket named by `address`. @@ -59,13 +59,13 @@ impl UnixStream { } /// Returns the socket address of the local half of this connection. - pub fn local_addr(&self) -> io::Result { - sys::uds::stream::local_addr(&self.inner) + pub fn local_addr(&self) -> io::Result { + self.inner.local_addr() } /// Returns the socket address of the remote half of this connection. - pub fn peer_addr(&self) -> io::Result { - sys::uds::stream::peer_addr(&self.inner) + pub fn peer_addr(&self) -> io::Result { + self.inner.peer_addr() } /// Returns the value of the `SO_ERROR` option. diff --git a/src/sys/mod.rs b/src/sys/mod.rs index f60c2091f..5f1ee9c9c 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -80,9 +80,4 @@ cfg_not_os_poll! { #[cfg(feature = "os-ext")] pub use self::unix::SourceFd; } - - #[cfg(unix)] - cfg_net! { - pub use self::unix::SocketAddr; - } } diff --git a/src/sys/shell/uds.rs b/src/sys/shell/uds.rs index bac547b03..446781aed 100644 --- a/src/sys/shell/uds.rs +++ b/src/sys/shell/uds.rs @@ -1,10 +1,8 @@ pub(crate) mod datagram { - use crate::net::SocketAddr; use std::io; - use std::os::unix::net; - use std::path::Path; + use std::os::unix::net::{self, SocketAddr}; - pub(crate) fn bind(_: &Path) -> io::Result { + pub(crate) fn bind_addr(_: &SocketAddr) -> io::Result { os_required!() } @@ -15,32 +13,13 @@ pub(crate) mod datagram { pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> { os_required!() } - - pub(crate) fn local_addr(_: &net::UnixDatagram) -> io::Result { - os_required!() - } - - pub(crate) fn peer_addr(_: &net::UnixDatagram) -> io::Result { - os_required!() - } - - pub(crate) fn recv_from( - _: &net::UnixDatagram, - _: &mut [u8], - ) -> io::Result<(usize, SocketAddr)> { - os_required!() - } } pub(crate) mod listener { - use crate::net::{SocketAddr, UnixStream}; use std::io; - use std::os::unix::net; - use std::path::Path; + use std::os::unix::net::{self, SocketAddr}; - pub(crate) fn bind(_: &Path) -> io::Result { - os_required!() - } + use crate::net::UnixStream; pub(crate) fn bind_addr(_: &SocketAddr) -> io::Result { os_required!() @@ -49,21 +28,11 @@ pub(crate) mod listener { pub(crate) fn accept(_: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> { os_required!() } - - pub(crate) fn local_addr(_: &net::UnixListener) -> io::Result { - os_required!() - } } pub(crate) mod stream { - use crate::net::SocketAddr; use std::io; - use std::os::unix::net; - use std::path::Path; - - pub(crate) fn connect(_: &Path) -> io::Result { - os_required!() - } + use std::os::unix::net::{self, SocketAddr}; pub(crate) fn connect_addr(_: &SocketAddr) -> io::Result { os_required!() @@ -72,12 +41,4 @@ pub(crate) mod stream { pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> { os_required!() } - - pub(crate) fn local_addr(_: &net::UnixStream) -> io::Result { - os_required!() - } - - pub(crate) fn peer_addr(_: &net::UnixStream) -> io::Result { - os_required!() - } } diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index aa48cc0f0..0e8e24070 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -31,8 +31,6 @@ cfg_os_poll! { pub(crate) mod udp; #[cfg(not(target_os = "hermit"))] pub(crate) mod uds; - #[cfg(not(target_os = "hermit"))] - pub use self::uds::SocketAddr; } cfg_io_source! { @@ -118,11 +116,6 @@ cfg_os_poll! { } cfg_not_os_poll! { - cfg_net! { - mod uds; - pub use self::uds::SocketAddr; - } - cfg_any_os_ext! { mod sourcefd; #[cfg(feature = "os-ext")] diff --git a/src/sys/unix/uds/datagram.rs b/src/sys/unix/uds/datagram.rs index 5853a4d50..0436cbff2 100644 --- a/src/sys/unix/uds/datagram.rs +++ b/src/sys/unix/uds/datagram.rs @@ -1,18 +1,16 @@ -use super::{socket_addr, SocketAddr}; -use crate::sys::unix::net::new_socket; - use std::io; -use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::os::unix::net; -use std::path::Path; +use std::os::unix::net::{self, SocketAddr}; -pub(crate) fn bind(path: &Path) -> io::Result { - let (sockaddr, socklen) = socket_addr(path.as_os_str().as_bytes())?; - let sockaddr = &sockaddr as *const libc::sockaddr_un as *const _; +use crate::sys::unix::net::new_socket; +use crate::sys::unix::uds::unix_addr; +pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result { let socket = unbound()?; - syscall!(bind(socket.as_raw_fd(), sockaddr, socklen))?; + + let (unix_address, addrlen) = unix_addr(address); + let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr; + syscall!(bind(socket.as_raw_fd(), sockaddr, addrlen))?; Ok(socket) } @@ -25,33 +23,3 @@ pub(crate) fn unbound() -> io::Result { 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 { - super::local_addr(socket.as_raw_fd()) -} - -pub(crate) fn peer_addr(socket: &net::UnixDatagram) -> io::Result { - 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/src/sys/unix/uds/listener.rs b/src/sys/unix/uds/listener.rs index b94243720..444e3f98a 100644 --- a/src/sys/unix/uds/listener.rs +++ b/src/sys/unix/uds/listener.rs @@ -1,46 +1,36 @@ -use super::socket_addr; -use crate::net::{SocketAddr, UnixStream}; -use crate::sys::unix::net::new_socket; +use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::os::unix::net; +use std::os::unix::net::{self, SocketAddr}; use std::path::Path; use std::{io, mem}; -pub(crate) fn bind(path: &Path) -> io::Result { - let socket_address = { - let (sockaddr, socklen) = socket_addr(path.as_os_str().as_bytes())?; - SocketAddr::from_parts(sockaddr, socklen) - }; - - bind_addr(&socket_address) -} +use crate::net::UnixStream; +use crate::sys::unix::net::new_socket; +use crate::sys::unix::uds::{path_offset, unix_addr}; pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result { let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; let socket = unsafe { net::UnixListener::from_raw_fd(fd) }; - let sockaddr = address.raw_sockaddr() as *const libc::sockaddr_un as *const libc::sockaddr; - syscall!(bind(fd, sockaddr, *address.raw_socklen()))?; + let (unix_address, addrlen) = unix_addr(address); + let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr; + syscall!(bind(fd, sockaddr, addrlen))?; syscall!(listen(fd, 1024))?; Ok(socket) } pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> { - let sockaddr = mem::MaybeUninit::::zeroed(); - - // This is safe to assume because a `libc::sockaddr_un` filled with `0` - // bytes is properly initialized. + // SAFETY: `libc::sockaddr_un` zero filled 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() }; + let mut sockaddr = unsafe { mem::zeroed::() }; - 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( @@ -106,11 +96,18 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So Ok(s) }); - socket - .map(UnixStream::from_std) - .map(|stream| (stream, SocketAddr::from_parts(sockaddr, socklen))) -} + let socket = socket.map(UnixStream::from_std)?; -pub(crate) fn local_addr(listener: &net::UnixListener) -> io::Result { - super::local_addr(listener.as_raw_fd()) + #[allow(unused_mut)] // See below. + let mut path_len = socklen as usize - path_offset(&sockaddr); + // On FreeBSD and Darwin, it returns a length of 14/16, but an unnamed (all + // zero) address. Map that to a length of 0 to match other OS. + if sockaddr.sun_path[0] == 0 { + path_len = 0; + } + let address = SocketAddr::from_pathname(Path::new(OsStr::from_bytes(unsafe { + // SAFETY: going from i8 to u8 is fine in this context. + &*(&sockaddr.sun_path[..path_len] as *const [libc::c_char] as *const [u8]) + })))?; + Ok((socket, address)) } diff --git a/src/sys/unix/uds/mod.rs b/src/sys/unix/uds/mod.rs index 20367fd09..42508612b 100644 --- a/src/sys/unix/uds/mod.rs +++ b/src/sys/unix/uds/mod.rs @@ -1,5 +1,17 @@ -mod socketaddr; -pub use self::socketaddr::SocketAddr; +#[cfg(target_os = "android")] +use std::os::android::net::SocketAddrExt; +#[cfg(target_os = "linux")] +use std::os::linux::net::SocketAddrExt; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::io::FromRawFd; +use std::os::unix::net::SocketAddr; +use std::{io, mem, ptr}; + +pub(crate) mod datagram; +pub(crate) mod listener; +pub(crate) mod stream; + +const UNNAMED_ADDRESS: &[u8] = &[]; /// Get the `sun_path` field offset of `sockaddr_un` for the target OS. /// @@ -7,162 +19,150 @@ pub use self::socketaddr::SocketAddr; /// `size_of::()`, 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 { +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::io::{RawFd, FromRawFd}; - use std::{io, mem}; - - pub(crate) mod datagram; - pub(crate) mod listener; - pub(crate) mod stream; - - pub(in crate::sys) fn socket_addr(bytes: &[u8]) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { - let sockaddr = mem::MaybeUninit::::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; - - match (bytes.first(), 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", - )); +/// Converts a Rust `SocketAddr` into the system representation. +fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) { + // SAFETY: `libc::sockaddr_un` zero filled 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 { mem::zeroed::() }; + + sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + #[allow(unused_mut)] // Only used with abstract namespaces. + let mut offset = 0; + let addr = match address.as_pathname() { + Some(path) => path.as_os_str().as_bytes(), + #[cfg(any(target_os = "android", target_os = "linux"))] + None => match address.as_abstract_name() { + Some(name) => { + offset += 1; + name } - (_, Ordering::Greater) | (_, Ordering::Equal) => { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "path must be shorter than libc::sockaddr_un.sun_path", - )); - } - _ => {} - } + None => UNNAMED_ADDRESS, + }, + #[cfg(not(any(target_os = "android", target_os = "linux")))] + None => UNNAMED_ADDRESS, + }; + + // SAFETY: `addr` and `sockaddr.sun_path` are not overlapping and both point + // to valid memory. + // SAFETY: since `addr` is a valid Unix address, it must not be larger than + // `SUN_LEN` bytes, thus we won't overwrite the size of sockaddr.sun_path. + // SAFETY: null byte is already written because we zeroed the address above. + debug_assert!(offset + addr.len() <= sockaddr.sun_path.len()); + unsafe { + ptr::copy_nonoverlapping( + addr.as_ptr(), + sockaddr.sun_path.as_mut_ptr().add(offset).cast(), + addr.len(), + ) + }; + + let mut addrlen = path_offset(&sockaddr) + addr.len(); + // +1 for null byte at the end of the path, not needed for abstract + // namespaces (which start with a null byte). + match addr.first() { + Some(&0) | None => {} + Some(_) => addrlen += 1, + } - for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) { - *dst = *src as libc::c_char; - } + // SAFETY: the length is fine to cast to `socklen_t` as it's 32 bits and the + // address can be at most `SUN_LEN` bytes. + (sockaddr, addrlen as _) +} - let offset = path_offset(&sockaddr); - let mut socklen = offset + bytes.len(); +fn pair(flags: libc::c_int) -> io::Result<(T, T)> +where + T: FromRawFd, +{ + #[cfg(not(any( + target_os = "aix", + target_os = "ios", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", + target_os = "espidf", + target_os = "vita", + )))] + 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 (and others) 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 = "aix", + target_os = "ios", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", + target_os = "espidf", + target_os = "vita", + ))] + { + syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; + #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))] + syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; + syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; + #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))] + syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; + } - match bytes.first() { - // The struct has already been zeroes so the null byte for pathname - // addresses is already there. - Some(&0) | None => {} - Some(_) => socklen += 1, - } + Ok(pair) +} - Ok((sockaddr, socklen as libc::socklen_t)) - } +#[cfg(test)] +mod tests { + use std::os::unix::net::SocketAddr; + use std::path::Path; + use std::str; - fn pair(flags: libc::c_int) -> io::Result<(T, T)> - where T: FromRawFd, - { - #[cfg(not(any( - target_os = "aix", - target_os = "ios", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "vita", - target_os = "nto", - )))] - let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; - #[cfg(target_os = "nto")] - let flags = flags | 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 (and others) 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 = "aix", - target_os = "ios", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "vita", - target_os = "nto", - ))] - { - syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; - #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))] - syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; - syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; - #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))] - syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; - } - - Ok(pair) - } + use super::{path_offset, unix_addr}; - // The following functions can't simply be replaced with a call to - // `net::UnixDatagram` because of our `SocketAddr` type. + #[test] + fn pathname_address() { + const PATH: &str = "./foo/bar.txt"; + const PATH_LEN: usize = 13; - fn local_addr(socket: RawFd) -> io::Result { - SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) + // Pathname addresses do have a null terminator, so `socklen` is + // expected to be `PATH_LEN` + `offset` + 1. + let address = SocketAddr::from_pathname(Path::new(PATH)).unwrap(); + let (sockaddr, actual) = unix_addr(&address); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset + 1; + assert_eq!(expected as libc::socklen_t, actual) } - fn peer_addr(socket: RawFd) -> io::Result { - SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen))) - } + #[test] + #[cfg(any(target_os = "android", target_os = "linux"))] + fn abstract_address() { + use std::os::linux::net::SocketAddrExt; - #[cfg(test)] - mod tests { - use super::{path_offset, socket_addr}; - use std::os::unix::ffi::OsStrExt; - 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.as_os_str().as_bytes()).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 (sockaddr, actual) = socket_addr(PATH).unwrap(); - let offset = path_offset(&sockaddr); - let expected = PATH_LEN + offset; - assert_eq!(expected as libc::socklen_t, actual) - } + 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 address = SocketAddr::from_abstract_name(PATH).unwrap(); + let (sockaddr, actual) = unix_addr(&address); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset; + assert_eq!(expected as libc::socklen_t, actual) } } diff --git a/src/sys/unix/uds/socketaddr.rs b/src/sys/unix/uds/socketaddr.rs deleted file mode 100644 index 8e0ef53a4..000000000 --- a/src/sys/unix/uds/socketaddr.rs +++ /dev/null @@ -1,138 +0,0 @@ -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) -> io::Result - where - F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result, - { - let mut sockaddr = { - let sockaddr = mem::MaybeUninit::::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 } - } - - pub(crate) fn raw_sockaddr(&self) -> &libc::sockaddr_un { - &self.sockaddr - } - - pub(crate) fn raw_socklen(&self) -> &libc::socklen_t { - &self.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/src/sys/unix/uds/stream.rs b/src/sys/unix/uds/stream.rs index 261d27b0d..3e5434a91 100644 --- a/src/sys/unix/uds/stream.rs +++ b/src/sys/unix/uds/stream.rs @@ -1,27 +1,17 @@ -use super::{socket_addr, SocketAddr}; -use crate::sys::unix::net::new_socket; - use std::io; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::os::unix::net; -use std::path::Path; - -pub(crate) fn connect(path: &Path) -> io::Result { - let socket_address = { - let (sockaddr, socklen) = socket_addr(path.as_os_str().as_bytes())?; - SocketAddr::from_parts(sockaddr, socklen) - }; +use std::os::unix::io::FromRawFd; +use std::os::unix::net::{self, SocketAddr}; - connect_addr(&socket_address) -} +use crate::sys::unix::net::new_socket; +use crate::sys::unix::uds::unix_addr; pub(crate) fn connect_addr(address: &SocketAddr) -> io::Result { let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; let socket = unsafe { net::UnixStream::from_raw_fd(fd) }; - let sockaddr = address.raw_sockaddr() as *const libc::sockaddr_un as *const libc::sockaddr; - match syscall!(connect(fd, sockaddr, *address.raw_socklen())) { + let (unix_address, addrlen) = unix_addr(address); + let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr; + match syscall!(connect(fd, sockaddr, addrlen)) { Ok(_) => {} Err(ref err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {} Err(e) => return Err(e), @@ -33,11 +23,3 @@ pub(crate) fn connect_addr(address: &SocketAddr) -> io::Result 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 { - super::local_addr(socket.as_raw_fd()) -} - -pub(crate) fn peer_addr(socket: &net::UnixStream) -> io::Result { - super::peer_addr(socket.as_raw_fd()) -} diff --git a/tests/unix_listener.rs b/tests/unix_listener.rs index d1d9cf07d..c13e9cbb6 100644 --- a/tests/unix_listener.rs +++ b/tests/unix_listener.rs @@ -133,17 +133,51 @@ fn unix_listener_deregister() { handle.join().unwrap(); } -#[cfg(target_os = "linux")] #[test] +#[cfg(any(target_os = "android", target_os = "linux"))] fn unix_listener_abstract_namespace() { use rand::Rng; + use std::os::linux::net::SocketAddrExt; + use std::os::unix::net::SocketAddr; + + let (mut poll, mut events) = init_with_poll(); + let barrier = Arc::new(Barrier::new(2)); + let num: u64 = rand::thread_rng().gen(); - let name = format!("\u{0000}-mio-abstract-uds-{}", num); - let listener = UnixListener::bind(&name).unwrap(); + let name = format!("mio-abstract-uds-{}", num); + let address = SocketAddr::from_abstract_name(name.as_bytes()).unwrap(); + let mut listener = UnixListener::bind_addr(&address).unwrap(); assert_eq!( - listener.local_addr().unwrap().as_abstract_namespace(), - Some(&name.as_bytes()[1..]), + listener.local_addr().unwrap().as_abstract_name(), + address.as_abstract_name(), ); + + poll.registry() + .register( + &mut listener, + TOKEN_1, + Interest::WRITABLE.add(Interest::READABLE), + ) + .unwrap(); + expect_no_events(&mut poll, &mut events); + + let barrier2 = barrier.clone(); + let handle = thread::spawn(move || { + let conn = net::UnixStream::connect_addr(&address).unwrap(); + barrier2.wait(); + drop(conn); + }); + expect_events( + &mut poll, + &mut events, + vec![ExpectEvent::new(TOKEN_1, Interest::READABLE)], + ); + + let (_, address) = listener.accept().unwrap(); + assert!(address.is_unnamed()); + + barrier.wait(); + handle.join().unwrap(); } fn smoke_test(new_listener: F, test_name: &'static str)