tokio/net/unix/
socket.rs

1use std::io;
2use std::path::Path;
3
4use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
5
6use crate::net::{UnixDatagram, UnixListener, UnixStream};
7
8cfg_net_unix! {
9    /// A Unix socket that has not yet been converted to a [`UnixStream`], [`UnixDatagram`], or
10    /// [`UnixListener`].
11    ///
12    /// `UnixSocket` wraps an operating system socket and enables the caller to
13    /// configure the socket before establishing a connection or accepting
14    /// inbound connections. The caller is able to set socket option and explicitly
15    /// bind the socket with a socket address.
16    ///
17    /// The underlying socket is closed when the `UnixSocket` value is dropped.
18    ///
19    /// `UnixSocket` should only be used directly if the default configuration used
20    /// by [`UnixStream::connect`], [`UnixDatagram::bind`], and [`UnixListener::bind`]
21    /// does not meet the required use case.
22    ///
23    /// Calling `UnixStream::connect(path)` effectively performs the same function as:
24    ///
25    /// ```no_run
26    /// use tokio::net::UnixSocket;
27    /// use std::error::Error;
28    ///
29    /// #[tokio::main]
30    /// async fn main() -> Result<(), Box<dyn Error>> {
31    ///     let dir = tempfile::tempdir().unwrap();
32    ///     let path = dir.path().join("bind_path");
33    ///     let socket = UnixSocket::new_stream()?;
34    ///
35    ///     let stream = socket.connect(path).await?;
36    ///
37    ///     Ok(())
38    /// }
39    /// ```
40    ///
41    /// Calling `UnixDatagram::bind(path)` effectively performs the same function as:
42    ///
43    /// ```no_run
44    /// use tokio::net::UnixSocket;
45    /// use std::error::Error;
46    ///
47    /// #[tokio::main]
48    /// async fn main() -> Result<(), Box<dyn Error>> {
49    ///     let dir = tempfile::tempdir().unwrap();
50    ///     let path = dir.path().join("bind_path");
51    ///     let socket = UnixSocket::new_datagram()?;
52    ///     socket.bind(path)?;
53    ///
54    ///     let datagram = socket.datagram()?;
55    ///
56    ///     Ok(())
57    /// }
58    /// ```
59    ///
60    /// Calling `UnixListener::bind(path)` effectively performs the same function as:
61    ///
62    /// ```no_run
63    /// use tokio::net::UnixSocket;
64    /// use std::error::Error;
65    ///
66    /// #[tokio::main]
67    /// async fn main() -> Result<(), Box<dyn Error>> {
68    ///     let dir = tempfile::tempdir().unwrap();
69    ///     let path = dir.path().join("bind_path");
70    ///     let socket = UnixSocket::new_stream()?;
71    ///     socket.bind(path)?;
72    ///
73    ///     let listener = socket.listen(1024)?;
74    ///
75    ///     Ok(())
76    /// }
77    /// ```
78    ///
79    /// Setting socket options not explicitly provided by `UnixSocket` may be done by
80    /// accessing the [`RawFd`]/[`RawSocket`] using [`AsRawFd`]/[`AsRawSocket`] and
81    /// setting the option with a crate like [`socket2`].
82    ///
83    /// [`RawFd`]: std::os::fd::RawFd
84    /// [`RawSocket`]: https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html
85    /// [`AsRawFd`]: std::os::fd::AsRawFd
86    /// [`AsRawSocket`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html
87    /// [`socket2`]: https://docs.rs/socket2/
88    #[derive(Debug)]
89    pub struct UnixSocket {
90        inner: socket2::Socket,
91    }
92}
93
94impl UnixSocket {
95    fn ty(&self) -> socket2::Type {
96        self.inner.r#type().unwrap()
97    }
98
99    /// Creates a new Unix datagram socket.
100    ///
101    /// Calls `socket(2)` with `AF_UNIX` and `SOCK_DGRAM`.
102    ///
103    /// # Returns
104    ///
105    /// On success, the newly created [`UnixSocket`] is returned. If an error is
106    /// encountered, it is returned instead.
107    pub fn new_datagram() -> io::Result<UnixSocket> {
108        UnixSocket::new(socket2::Type::DGRAM)
109    }
110
111    /// Creates a new Unix stream socket.
112    ///
113    /// Calls `socket(2)` with `AF_UNIX` and `SOCK_STREAM`.
114    ///
115    /// # Returns
116    ///
117    /// On success, the newly created [`UnixSocket`] is returned. If an error is
118    /// encountered, it is returned instead.
119    pub fn new_stream() -> io::Result<UnixSocket> {
120        UnixSocket::new(socket2::Type::STREAM)
121    }
122
123    fn new(ty: socket2::Type) -> io::Result<UnixSocket> {
124        #[cfg(any(
125            target_os = "android",
126            target_os = "dragonfly",
127            target_os = "freebsd",
128            target_os = "fuchsia",
129            target_os = "illumos",
130            target_os = "linux",
131            target_os = "netbsd",
132            target_os = "openbsd"
133        ))]
134        let ty = ty.nonblocking();
135        let inner = socket2::Socket::new(socket2::Domain::UNIX, ty, None)?;
136        #[cfg(not(any(
137            target_os = "android",
138            target_os = "dragonfly",
139            target_os = "freebsd",
140            target_os = "fuchsia",
141            target_os = "illumos",
142            target_os = "linux",
143            target_os = "netbsd",
144            target_os = "openbsd"
145        )))]
146        inner.set_nonblocking(true)?;
147        Ok(UnixSocket { inner })
148    }
149
150    /// Binds the socket to the given address.
151    ///
152    /// This calls the `bind(2)` operating-system function.
153    pub fn bind(&self, path: impl AsRef<Path>) -> io::Result<()> {
154        let addr = socket2::SockAddr::unix(path)?;
155        self.inner.bind(&addr)
156    }
157
158    /// Converts the socket into a `UnixListener`.
159    ///
160    /// `backlog` defines the maximum number of pending connections are queued
161    /// by the operating system at any given time. Connection are removed from
162    /// the queue with [`UnixListener::accept`]. When the queue is full, the
163    /// operating-system will start rejecting connections.
164    ///
165    /// Calling this function on a socket created by [`new_datagram`] will return an error.
166    ///
167    /// This calls the `listen(2)` operating-system function, marking the socket
168    /// as a passive socket.
169    ///
170    /// [`new_datagram`]: `UnixSocket::new_datagram`
171    pub fn listen(self, backlog: u32) -> io::Result<UnixListener> {
172        if self.ty() == socket2::Type::DGRAM {
173            return Err(io::Error::new(
174                io::ErrorKind::Other,
175                "listen cannot be called on a datagram socket",
176            ));
177        }
178
179        self.inner.listen(backlog as i32)?;
180        let mio = {
181            use std::os::unix::io::{FromRawFd, IntoRawFd};
182
183            let raw_fd = self.inner.into_raw_fd();
184            unsafe { mio::net::UnixListener::from_raw_fd(raw_fd) }
185        };
186
187        UnixListener::new(mio)
188    }
189
190    /// Establishes a Unix connection with a peer at the specified socket address.
191    ///
192    /// The `UnixSocket` is consumed. Once the connection is established, a
193    /// connected [`UnixStream`] is returned. If the connection fails, the
194    /// encountered error is returned.
195    ///
196    /// Calling this function on a socket created by [`new_datagram`] will return an error.
197    ///
198    /// This calls the `connect(2)` operating-system function.
199    ///
200    /// [`new_datagram`]: `UnixSocket::new_datagram`
201    pub async fn connect(self, path: impl AsRef<Path>) -> io::Result<UnixStream> {
202        if self.ty() == socket2::Type::DGRAM {
203            return Err(io::Error::new(
204                io::ErrorKind::Other,
205                "connect cannot be called on a datagram socket",
206            ));
207        }
208
209        let addr = socket2::SockAddr::unix(path)?;
210        if let Err(err) = self.inner.connect(&addr) {
211            if err.raw_os_error() != Some(libc::EINPROGRESS) {
212                return Err(err);
213            }
214        }
215        let mio = {
216            use std::os::unix::io::{FromRawFd, IntoRawFd};
217
218            let raw_fd = self.inner.into_raw_fd();
219            unsafe { mio::net::UnixStream::from_raw_fd(raw_fd) }
220        };
221
222        UnixStream::connect_mio(mio).await
223    }
224
225    /// Converts the socket into a [`UnixDatagram`].
226    ///
227    /// Calling this function on a socket created by [`new_stream`] will return an error.
228    ///
229    /// [`new_stream`]: `UnixSocket::new_stream`
230    pub fn datagram(self) -> io::Result<UnixDatagram> {
231        if self.ty() == socket2::Type::STREAM {
232            return Err(io::Error::new(
233                io::ErrorKind::Other,
234                "datagram cannot be called on a stream socket",
235            ));
236        }
237        let mio = {
238            use std::os::unix::io::{FromRawFd, IntoRawFd};
239
240            let raw_fd = self.inner.into_raw_fd();
241            unsafe { mio::net::UnixDatagram::from_raw_fd(raw_fd) }
242        };
243
244        UnixDatagram::from_mio(mio)
245    }
246}
247
248impl AsRawFd for UnixSocket {
249    fn as_raw_fd(&self) -> RawFd {
250        self.inner.as_raw_fd()
251    }
252}
253
254impl AsFd for UnixSocket {
255    fn as_fd(&self) -> BorrowedFd<'_> {
256        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
257    }
258}
259
260impl FromRawFd for UnixSocket {
261    unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
262        let inner = socket2::Socket::from_raw_fd(fd);
263        UnixSocket { inner }
264    }
265}
266
267impl IntoRawFd for UnixSocket {
268    fn into_raw_fd(self) -> RawFd {
269        self.inner.into_raw_fd()
270    }
271}