Skip to main content

nuclei/net/
unix_socket.rs

1//! [`UnixSocket`]: a Unix domain socket made and bound before it becomes a
2//! [`UnixStream`](super::UnixStream) (`connect`), a
3//! [`UnixListener`](super::UnixListener) (`listen`) or a
4//! [`UnixDatagram`](super::UnixDatagram) (`datagram`).
5//!
6//! As [`TcpSocket`](super::TcpSocket), it is its descriptor alone.
7
8use std::io;
9use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
10use std::os::unix::net::{UnixDatagram, UnixListener};
11use std::path::Path;
12
13use crate::io::source::Kind;
14use crate::io::{Handle, sys};
15
16/// A Unix domain socket, of streams or of datagrams, to bind before it
17/// connects, listens or sends.
18///
19/// ```
20/// use nuclei::net::UnixSocket;
21///
22/// # nuclei::block_on(async {
23/// let dir = std::env::temp_dir().join(format!("nuclei-doc-unix-socket-{}", std::process::id()));
24/// let _ = std::fs::remove_file(&dir);
25/// let listener = UnixSocket::new_stream()?;
26/// listener.bind(&dir)?;
27/// let listener = listener.listen(16)?;
28/// let client = UnixSocket::new_stream()?.connect(&dir).await?;
29/// let (_server, _) = listener.accept().await?;
30/// client.send(b"hi").await?;
31/// std::fs::remove_file(&dir)?;
32/// # std::io::Result::Ok(())
33/// # }).unwrap();
34/// ```
35pub struct UnixSocket {
36    socket: OwnedFd,
37    datagrams: bool,
38}
39
40impl UnixSocket {
41    /// A new socket of streams.
42    ///
43    /// # Errors
44    ///
45    /// When the system has no socket to give.
46    pub fn new_stream() -> io::Result<UnixSocket> {
47        Self::new(false)
48    }
49
50    /// A new socket of datagrams.
51    ///
52    /// # Errors
53    ///
54    /// When the system has no socket to give.
55    pub fn new_datagram() -> io::Result<UnixSocket> {
56        Self::new(true)
57    }
58
59    fn new(datagrams: bool) -> io::Result<UnixSocket> {
60        let ty = if datagrams {
61            libc::SOCK_DGRAM
62        } else {
63            libc::SOCK_STREAM
64        };
65        Ok(UnixSocket {
66            socket: sys::socket(libc::AF_UNIX, ty)?,
67            datagrams,
68        })
69    }
70
71    /// Binds the socket to `path`, which must not exist yet.
72    ///
73    /// # Errors
74    ///
75    /// As `bind(2)`: the path exists, or is too long.
76    pub fn bind(&self, path: impl AsRef<Path>) -> io::Result<()> {
77        sys::bind_unix(
78            self.socket.as_raw_fd(),
79            &sys::UnixAddr::path(path.as_ref())?,
80        )
81    }
82
83    /// Makes a socket of streams a listener, with room for `backlog`
84    /// connections not accepted yet.
85    ///
86    /// # Errors
87    ///
88    /// On a socket of datagrams, and as `listen(2)`.
89    pub fn listen(self, backlog: u32) -> io::Result<super::UnixListener> {
90        if self.datagrams {
91            return Err(wrong_kind("a socket of datagrams cannot listen"));
92        }
93        sys::listen(self.socket.as_raw_fd(), backlog)?;
94        Ok(Handle::from_nonblocking(
95            UnixListener::from(self.socket),
96            Kind::UnixListener,
97        ))
98    }
99
100    /// Connects a socket of streams to the listener at `path`.
101    ///
102    /// # Errors
103    ///
104    /// On a socket of datagrams, and as `connect(2)`.
105    pub async fn connect(self, path: impl AsRef<Path>) -> io::Result<super::UnixStream> {
106        if self.datagrams {
107            return Err(wrong_kind(
108                "a socket of datagrams connects as a datagram socket",
109            ));
110        }
111        super::unix::connect(self.socket, &sys::UnixAddr::path(path.as_ref())?).await
112    }
113
114    /// Makes a socket of datagrams a datagram socket, bound or not.
115    ///
116    /// # Errors
117    ///
118    /// On a socket of streams.
119    pub fn datagram(self) -> io::Result<super::UnixDatagram> {
120        if !self.datagrams {
121            return Err(wrong_kind("a socket of streams is not a datagram socket"));
122        }
123        Ok(Handle::from_nonblocking(
124            UnixDatagram::from(self.socket),
125            Kind::UnixDatagram,
126        ))
127    }
128}
129
130fn wrong_kind(what: &'static str) -> io::Error {
131    io::Error::new(io::ErrorKind::InvalidInput, what)
132}
133
134impl core::fmt::Debug for UnixSocket {
135    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136        f.debug_struct("UnixSocket")
137            .field("fd", &self.socket.as_raw_fd())
138            .field("datagrams", &self.datagrams)
139            .finish()
140    }
141}
142
143impl AsRawFd for UnixSocket {
144    fn as_raw_fd(&self) -> RawFd {
145        self.socket.as_raw_fd()
146    }
147}
148
149impl AsFd for UnixSocket {
150    fn as_fd(&self) -> BorrowedFd<'_> {
151        self.socket.as_fd()
152    }
153}
154
155impl IntoRawFd for UnixSocket {
156    fn into_raw_fd(self) -> RawFd {
157        self.socket.into_raw_fd()
158    }
159}
160
161impl FromRawFd for UnixSocket {
162    /// # Safety
163    ///
164    /// `fd` is an open, non-blocking Unix domain socket the caller owns and
165    /// hands over.
166    unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
167        // SAFETY: the caller's contract.
168        let socket = unsafe { OwnedFd::from_raw_fd(fd) };
169        let mut ty: libc::c_int = 0;
170        let mut len = size_of::<libc::c_int>() as libc::socklen_t;
171        // SAFETY: SO_TYPE is an int, written into our own.
172        let r = unsafe {
173            libc::getsockopt(
174                fd,
175                libc::SOL_SOCKET,
176                libc::SO_TYPE,
177                core::ptr::from_mut(&mut ty).cast(),
178                &mut len,
179            )
180        };
181        UnixSocket {
182            socket,
183            datagrams: r == 0 && ty == libc::SOCK_DGRAM,
184        }
185    }
186}