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: Socket = 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}
272