use std::io; use std::path::Path; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::net::{UnixDatagram, UnixListener, UnixStream}; cfg_net_unix! { /// A Unix socket that has not yet been converted to a [`UnixStream`], [`UnixDatagram`], or /// [`UnixListener`]. /// /// `UnixSocket` wraps an operating system socket and enables the caller to /// configure the socket before establishing a connection or accepting /// inbound connections. The caller is able to set socket option and explicitly /// bind the socket with a socket address. /// /// The underlying socket is closed when the `UnixSocket` value is dropped. /// /// `UnixSocket` should only be used directly if the default configuration used /// by [`UnixStream::connect`], [`UnixDatagram::bind`], and [`UnixListener::bind`] /// does not meet the required use case. /// /// Calling `UnixStream::connect(path)` effectively performs the same function as: /// /// ```no_run /// use tokio::net::UnixSocket; /// use std::error::Error; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { /// let dir = tempfile::tempdir().unwrap(); /// let path = dir.path().join("bind_path"); /// let socket = UnixSocket::new_stream()?; /// /// let stream = socket.connect(path).await?; /// /// Ok(()) /// } /// ``` /// /// Calling `UnixDatagram::bind(path)` effectively performs the same function as: /// /// ```no_run /// use tokio::net::UnixSocket; /// use std::error::Error; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { /// let dir = tempfile::tempdir().unwrap(); /// let path = dir.path().join("bind_path"); /// let socket = UnixSocket::new_datagram()?; /// socket.bind(path)?; /// /// let datagram = socket.datagram()?; /// /// Ok(()) /// } /// ``` /// /// Calling `UnixListener::bind(path)` effectively performs the same function as: /// /// ```no_run /// use tokio::net::UnixSocket; /// use std::error::Error; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { /// let dir = tempfile::tempdir().unwrap(); /// let path = dir.path().join("bind_path"); /// let socket = UnixSocket::new_stream()?; /// socket.bind(path)?; /// /// let listener = socket.listen(1024)?; /// /// Ok(()) /// } /// ``` /// /// Setting socket options not explicitly provided by `UnixSocket` may be done by /// accessing the [`RawFd`]/[`RawSocket`] using [`AsRawFd`]/[`AsRawSocket`] and /// setting the option with a crate like [`socket2`]. /// /// [`RawFd`]: std::os::fd::RawFd /// [`RawSocket`]: https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html /// [`AsRawFd`]: std::os::fd::AsRawFd /// [`AsRawSocket`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html /// [`socket2`]: https://docs.rs/socket2/ #[derive(Debug)] pub struct UnixSocket { inner: socket2::Socket, } } impl UnixSocket { fn ty(&self) -> socket2::Type { self.inner.r#type().unwrap() } /// Creates a new Unix datagram socket. /// /// Calls `socket(2)` with `AF_UNIX` and `SOCK_DGRAM`. /// /// # Returns /// /// On success, the newly created [`UnixSocket`] is returned. If an error is /// encountered, it is returned instead. pub fn new_datagram() -> io::Result { UnixSocket::new(socket2::Type::DGRAM) } /// Creates a new Unix stream socket. /// /// Calls `socket(2)` with `AF_UNIX` and `SOCK_STREAM`. /// /// # Returns /// /// On success, the newly created [`UnixSocket`] is returned. If an error is /// encountered, it is returned instead. pub fn new_stream() -> io::Result { UnixSocket::new(socket2::Type::STREAM) } fn new(ty: socket2::Type) -> io::Result { #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd" ))] let ty = ty.nonblocking(); let inner = socket2::Socket::new(socket2::Domain::UNIX, ty, None)?; #[cfg(not(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd" )))] inner.set_nonblocking(true)?; Ok(UnixSocket { inner }) } /// Binds the socket to the given address. /// /// This calls the `bind(2)` operating-system function. pub fn bind(&self, path: impl AsRef) -> io::Result<()> { let addr = socket2::SockAddr::unix(path)?; self.inner.bind(&addr) } /// Converts the socket into a `UnixListener`. /// /// `backlog` defines the maximum number of pending connections are queued /// by the operating system at any given time. Connection are removed from /// the queue with [`UnixListener::accept`]. When the queue is full, the /// operating-system will start rejecting connections. /// /// Calling this function on a socket created by [`new_datagram`] will return an error. /// /// This calls the `listen(2)` operating-system function, marking the socket /// as a passive socket. /// /// [`new_datagram`]: `UnixSocket::new_datagram` pub fn listen(self, backlog: u32) -> io::Result { if self.ty() == socket2::Type::DGRAM { return Err(io::Error::new( io::ErrorKind::Other, "listen cannot be called on a datagram socket", )); } self.inner.listen(backlog as i32)?; let mio = { use std::os::unix::io::{FromRawFd, IntoRawFd}; let raw_fd = self.inner.into_raw_fd(); unsafe { mio::net::UnixListener::from_raw_fd(raw_fd) } }; UnixListener::new(mio) } /// Establishes a Unix connection with a peer at the specified socket address. /// /// The `UnixSocket` is consumed. Once the connection is established, a /// connected [`UnixStream`] is returned. If the connection fails, the /// encountered error is returned. /// /// Calling this function on a socket created by [`new_datagram`] will return an error. /// /// This calls the `connect(2)` operating-system function. /// /// [`new_datagram`]: `UnixSocket::new_datagram` pub async fn connect(self, path: impl AsRef) -> io::Result { if self.ty() == socket2::Type::DGRAM { return Err(io::Error::new( io::ErrorKind::Other, "connect cannot be called on a datagram socket", )); } let addr = socket2::SockAddr::unix(path)?; if let Err(err) = self.inner.connect(&addr) { if err.raw_os_error() != Some(libc::EINPROGRESS) { return Err(err); } } let mio = { use std::os::unix::io::{FromRawFd, IntoRawFd}; let raw_fd = self.inner.into_raw_fd(); unsafe { mio::net::UnixStream::from_raw_fd(raw_fd) } }; UnixStream::connect_mio(mio).await } /// Converts the socket into a [`UnixDatagram`]. /// /// Calling this function on a socket created by [`new_stream`] will return an error. /// /// [`new_stream`]: `UnixSocket::new_stream` pub fn datagram(self) -> io::Result { if self.ty() == socket2::Type::STREAM { return Err(io::Error::new( io::ErrorKind::Other, "datagram cannot be called on a stream socket", )); } let mio = { use std::os::unix::io::{FromRawFd, IntoRawFd}; let raw_fd = self.inner.into_raw_fd(); unsafe { mio::net::UnixDatagram::from_raw_fd(raw_fd) } }; UnixDatagram::from_mio(mio) } } impl AsRawFd for UnixSocket { fn as_raw_fd(&self) -> RawFd { self.inner.as_raw_fd() } } impl AsFd for UnixSocket { fn as_fd(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } } } impl FromRawFd for UnixSocket { unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket { let inner = socket2::Socket::from_raw_fd(fd); UnixSocket { inner } } } impl IntoRawFd for UnixSocket { fn into_raw_fd(self) -> RawFd { self.inner.into_raw_fd() } }