diff --git a/tokio/src/net/unix/datagram/socket.rs b/tokio/src/net/unix/datagram/socket.rs index a57be3c9196..f54bb64f84b 100644 --- a/tokio/src/net/unix/datagram/socket.rs +++ b/tokio/src/net/unix/datagram/socket.rs @@ -5,6 +5,8 @@ use crate::util::check_socket_for_blocking; use std::fmt; use std::io; use std::net::Shutdown; +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use std::os::unix::net; use std::path::Path; @@ -396,7 +398,8 @@ impl UnixDatagram { where P: AsRef, { - let socket = mio::net::UnixDatagram::bind(path)?; + let addr = SocketAddr::from_path(path)?.0; + let socket = mio::net::UnixDatagram::bind_addr(&addr)?; UnixDatagram::new(socket) } @@ -571,6 +574,11 @@ impl UnixDatagram { /// The `send` method may be used to send data to the specified address. /// `recv` and `recv_from` will only receive data from that address. /// + /// # Limitations + /// + /// This method currently does not allow specifying Linux abstract + /// namespace paths. + /// /// # Examples /// ``` /// # use std::error::Error; @@ -605,6 +613,16 @@ impl UnixDatagram { /// # } /// ``` pub fn connect>(&self, path: P) -> io::Result<()> { + //FIXME: Convert this to `mio::net::UnixDatagram::connect_addr` once + // that is supported by MIO + #[cfg(any(target_os = "linux", target_os = "android"))] + if path.as_ref().as_os_str().as_bytes().starts_with(b"\0") { + return Err(io::Error::new( + io::ErrorKind::Other, + "UnixDatagram::connect(): abstract paths currently unsupported", + )); + } + self.io.connect(path) } @@ -694,6 +712,11 @@ impl UnixDatagram { /// Tries to send a datagram to the peer without waiting. /// + /// # Limitations + /// + /// This method currently does not allow specifying Linux abstract + /// namespace paths. + /// /// # Examples /// /// ```no_run @@ -733,6 +756,16 @@ impl UnixDatagram { where P: AsRef, { + //FIXME: Convert this to `mio::net::UnixDatagram::send_to_addr` once + // that is supported by MIO + #[cfg(any(target_os = "linux", target_os = "android"))] + if target.as_ref().as_os_str().as_bytes().starts_with(b"\0") { + return Err(io::Error::new( + io::ErrorKind::Other, + "UnixDatagram::try_send_to(): abstract paths currently unsupported", + )); + } + self.io .registration() .try_io(Interest::WRITABLE, || self.io.send_to(buf, target)) @@ -1066,6 +1099,11 @@ impl UnixDatagram { /// [`tokio::select!`](crate::select) statement and some other branch /// completes first, then it is guaranteed that the message was not sent. /// + /// # Limitations + /// + /// This method currently does not allow specifying Linux abstract + /// namespace paths. + /// /// # Examples /// ``` /// # use std::error::Error; @@ -1102,6 +1140,16 @@ impl UnixDatagram { where P: AsRef, { + //FIXME: Convert this to `mio::net::UnixDatagram::send_to_addr` once + // that is supported by MIO + #[cfg(any(target_os = "linux", target_os = "android"))] + if target.as_ref().as_os_str().as_bytes().starts_with(b"\0") { + return Err(io::Error::new( + io::ErrorKind::Other, + "UnixDatagram::send_to(): abstract paths currently unsupported", + )); + } + self.io .registration() .async_io(Interest::WRITABLE, || self.io.send_to(buf, target.as_ref())) @@ -1205,6 +1253,11 @@ impl UnixDatagram { /// `Waker` from the `Context` passed to the most recent call will be scheduled to /// receive a wakeup. /// + /// # Limitations + /// + /// This method currently does not allow specifying Linux abstract + /// namespace paths. + /// /// # Return value /// /// The function returns: @@ -1225,6 +1278,16 @@ impl UnixDatagram { where P: AsRef, { + //FIXME: Convert this to `mio::net::UnixDatagram::send_to_addr` once + // that is supported by MIO + #[cfg(any(target_os = "linux", target_os = "android"))] + if target.as_ref().as_os_str().as_bytes().starts_with(b"\0") { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "UnixDatagram::send_to(): abstract paths currently unsupported", + ))); + } + self.io .registration() .poll_write_io(cx, || self.io.send_to(buf, target.as_ref())) diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs index b18e6611184..e4169856a7f 100644 --- a/tokio/src/net/unix/listener.rs +++ b/tokio/src/net/unix/listener.rs @@ -4,14 +4,8 @@ use crate::util::check_socket_for_blocking; use std::fmt; use std::io; -#[cfg(target_os = "android")] -use std::os::android::net::SocketAddrExt; -#[cfg(target_os = "linux")] -use std::os::linux::net::SocketAddrExt; -#[cfg(any(target_os = "linux", target_os = "android"))] -use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use std::os::unix::net::{self, SocketAddr as StdSocketAddr}; +use std::os::unix::net; use std::path::Path; use std::task::{ready, Context, Poll}; @@ -77,19 +71,7 @@ impl UnixListener { where P: AsRef, { - // For now, we handle abstract socket paths on linux here. - #[cfg(any(target_os = "linux", target_os = "android"))] - let addr = { - let os_str_bytes = path.as_ref().as_os_str().as_bytes(); - if os_str_bytes.starts_with(b"\0") { - StdSocketAddr::from_abstract_name(&os_str_bytes[1..])? - } else { - StdSocketAddr::from_pathname(path)? - } - }; - #[cfg(not(any(target_os = "linux", target_os = "android")))] - let addr = StdSocketAddr::from_pathname(path)?; - + let addr = SocketAddr::from_path(path)?.0; let listener = mio::net::UnixListener::bind_addr(&addr)?; let io = PollEvented::new(listener)?; Ok(UnixListener { io }) diff --git a/tokio/src/net/unix/socketaddr.rs b/tokio/src/net/unix/socketaddr.rs index fd7e3e236f5..cd5ed7eae73 100644 --- a/tokio/src/net/unix/socketaddr.rs +++ b/tokio/src/net/unix/socketaddr.rs @@ -1,4 +1,12 @@ use std::fmt; +use std::io; +#[cfg(target_os = "android")] +use std::os::android::net::SocketAddrExt; +#[cfg(target_os = "linux")] +use std::os::linux::net::SocketAddrExt; +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::unix::ffi::OsStrExt; +use std::os::unix::net::SocketAddr as StdSocketAddr; use std::path::Path; /// An address associated with a Tokio Unix socket. @@ -7,9 +15,33 @@ use std::path::Path; /// can convert to and from the standard library `SocketAddr` type using the /// [`From`] trait. #[derive(Clone)] -pub struct SocketAddr(pub(super) std::os::unix::net::SocketAddr); +pub struct SocketAddr(pub(super) StdSocketAddr); impl SocketAddr { + /// Creates an address from a path + /// + /// On Linux, allows the path to start with a leading `\0` to declare + /// a socket path within the abstract namespace. + pub fn from_path

(path: P) -> io::Result + where + P: AsRef, + { + // For now, we handle abstract socket paths on linux here. + #[cfg(any(target_os = "linux", target_os = "android"))] + let addr = { + let os_str_bytes = path.as_ref().as_os_str().as_bytes(); + if os_str_bytes.starts_with(b"\0") { + StdSocketAddr::from_abstract_name(&os_str_bytes[1..])? + } else { + StdSocketAddr::from_pathname(path)? + } + }; + #[cfg(not(any(target_os = "linux", target_os = "android")))] + let addr = StdSocketAddr::from_pathname(path)?; + + Ok(SocketAddr(addr)) + } + /// Returns `true` if the address is unnamed. /// /// Documentation reflected in [`SocketAddr`]. diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index 2391d54b46e..669c02f817d 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -9,14 +9,8 @@ use std::fmt; use std::future::poll_fn; use std::io::{self, Read, Write}; use std::net::Shutdown; -#[cfg(target_os = "android")] -use std::os::android::net::SocketAddrExt; -#[cfg(target_os = "linux")] -use std::os::linux::net::SocketAddrExt; -#[cfg(any(target_os = "linux", target_os = "android"))] -use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use std::os::unix::net::{self, SocketAddr as StdSocketAddr}; +use std::os::unix::net; use std::path::Path; use std::pin::Pin; use std::task::{Context, Poll}; @@ -73,19 +67,7 @@ impl UnixStream { where P: AsRef, { - // On linux, abstract socket paths need to be considered. - #[cfg(any(target_os = "linux", target_os = "android"))] - let addr = { - let os_str_bytes = path.as_ref().as_os_str().as_bytes(); - if os_str_bytes.starts_with(b"\0") { - StdSocketAddr::from_abstract_name(&os_str_bytes[1..])? - } else { - StdSocketAddr::from_pathname(path)? - } - }; - #[cfg(not(any(target_os = "linux", target_os = "android")))] - let addr = StdSocketAddr::from_pathname(path)?; - + let addr = SocketAddr::from_path(path)?.0; let stream = mio::net::UnixStream::connect_addr(&addr)?; let stream = UnixStream::new(stream)?; diff --git a/tokio/tests/uds_datagram.rs b/tokio/tests/uds_datagram.rs index 2dfdc76f380..27a3e92ca86 100644 --- a/tokio/tests/uds_datagram.rs +++ b/tokio/tests/uds_datagram.rs @@ -8,6 +8,10 @@ use tokio::try_join; use std::future::poll_fn; use std::io; +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::linux::net::SocketAddrExt; +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::unix::net as std_net; use std::sync::Arc; async fn echo_server(socket: UnixDatagram) -> io::Result<()> { @@ -71,6 +75,50 @@ async fn echo_from() -> io::Result<()> { Ok(()) } +#[allow(clippy::bool_assert_comparison)] +#[tokio::test] +#[cfg_attr(miri, ignore)] // No `socket` on miri. +async fn recv_abstract() -> io::Result<()> { + // Generate 128-bit random string prefixed by `\0` as socket path + let abstract_path = format!( + "\0{:016x}{:016x}", + rand::random::(), + rand::random::() + ); + + // On non-Linux platforms, abstract socket paths are rejected + #[cfg(not(any(target_os = "linux", target_os = "android")))] + assert_eq!( + UnixDatagram::bind(&abstract_path).err().map(|e| e.kind()), + Some(io::ErrorKind::InvalidInput) + ); + + // Remaining test is Linux-only + #[cfg(any(target_os = "linux", target_os = "android"))] + { + // Create socket in abstract namespace + let recv_socket = UnixDatagram::bind(&abstract_path)?; + + // Send message from stdlib + // + //FIXME: Use `tokio` native here once passing abstract socket paths to + // `send_to`/`connect` is implemented + let send_socket = std_net::UnixDatagram::unbound()?; + send_socket.send_to_addr( + b"READY=1\n", + &std_net::SocketAddr::from_abstract_name(&abstract_path[1..])?, + )?; + + // Receive and validate message + let mut buf = vec![0u8; 32]; + let (len, addr) = recv_socket.recv_from(&mut buf).await?; + assert_eq!(&buf[0..len], b"READY=1\n"); + assert_eq!(addr.is_unnamed(), true); + } + + Ok(()) +} + // Even though we use sync non-blocking io we still need a reactor. #[tokio::test] #[cfg_attr(miri, ignore)] // No SOCK_DGRAM for `socketpair` in miri.