Skip to main content

uds_fork/
traits.rs

1
2use std::
3{
4    ffi::c_void, io::{self, IoSlice, IoSliceMut}, os::
5    {
6        fd::{AsFd, OwnedFd}, 
7        unix::{io::{AsRawFd, FromRawFd}, net::{UnixDatagram, UnixListener, UnixStream}} 
8    }
9};
10
11use libc::{MSG_PEEK, recvfrom, sendto};
12
13use crate::addr::UnixSocketAddr;
14use crate::helpers::*;
15use crate::ancillary::*;
16use crate::credentials::*;
17
18/// Extension trait for `std::os::unix::net::UnixStream` and nonblocking equivalents.
19pub trait UnixStreamExt: AsFd + AsRawFd + FromRawFd 
20{
21    /// Get the address of this socket, as a type that fully supports abstract addresses.
22    fn local_unix_addr(&self) -> Result<UnixSocketAddr, io::Error> 
23    {
24        get_unix_local_addr(self)
25    }
26
27    /// Returns the address of the other end of this stream,
28    /// as a type that fully supports abstract addresses.
29    fn peer_unix_addr(&self) -> Result<UnixSocketAddr, io::Error> 
30    {
31        get_unix_peer_addr(self)
32    }
33
34    /// Creates a connection to a listening path-based or abstract named socket.
35    fn connect_to_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error> where Self: Sized;
36
37    /// Creates a path-based or abstract-named socket and connects to a listening socket.
38    fn connect_from_to_unix_addr(from: &UnixSocketAddr, to: &UnixSocketAddr) -> Result<Self, io::Error> 
39        where Self: Sized;
40
41    /// Sends file descriptors in addition to bytes.
42    fn send_fds(&self, bytes: &[u8], fds: Vec<OwnedFd>) -> Result<usize, io::Error> 
43    {
44        send_ancillary(self, None, 0, &[IoSlice::new(bytes)], fds, None)
45    }
46
47    /// Receives file descriptors in addition to bytes.
48    fn recv_fds(&self, buf: &mut[u8], fd_buf: &mut Vec<OwnedFd>) -> Result<(usize, usize), io::Error> 
49    {
50        recv_fds(self, None, &mut[IoSliceMut::new(buf)], Some(fd_buf))
51            .map(|(bytes, _, fds)| (bytes, fds) )
52    }
53
54    /// Returns the credentials of the process that created the other end of this stream.
55    fn initial_peer_credentials(&self) -> Result<ConnCredentials, io::Error> 
56    {
57        peer_credentials(self)
58    }
59    /// Returns the SELinux security context of the process that created the other end of this stream.
60    ///
61    /// Will return an error on other operating systems than Linux or Android,
62    /// and also if running under kubernetes.
63    /// On success the number of bytes used is returned. (like `Read`)
64    ///
65    /// The default security context is `unconfined`, without any trailing NUL.  
66    /// A buffor of 50 bytes is probably always big enough.
67    fn initial_peer_selinux_context(&self, buffer: &mut[u8]) -> Result<usize, io::Error> 
68    {
69        selinux_context(self, buffer)
70    }
71}
72
73impl UnixStreamExt for UnixStream 
74{
75    fn connect_to_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error> 
76    {
77        let socket = Socket::<SocketStream>::new(false)?;
78        socket.set_unix_peer_addr(addr)?;
79        
80        return Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
81    }
82
83    fn connect_from_to_unix_addr(from: &UnixSocketAddr,  to: &UnixSocketAddr) -> Result<Self, io::Error> 
84    {
85        let socket = Socket::<SocketStream>::new(false)?;
86        socket.set_unix_local_addr(from)?;
87        socket.set_unix_peer_addr(to)?;
88        
89        return Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
90    }
91}
92
93/// Extension trait for using [`UnixSocketAddr`](struct.UnixSocketAddr.html) with `UnixListener` types.
94pub trait UnixListenerExt: AsFd + AsRawFd + FromRawFd 
95{
96    /// The type represeting the stream connection returned by `accept_unix_addr()`.
97    type Conn: FromRawFd;
98
99    /// Creates a socket bound to a `UnixSocketAddr` and starts listening on it.
100    fn bind_unix_addr(on: &UnixSocketAddr) -> Result<Self, io::Error> 
101        where Self: Sized;
102
103    /// Returns the address this socket is listening on.
104    fn local_unix_addr(&self) -> Result<UnixSocketAddr, io::Error> 
105    {
106        get_unix_local_addr(self)
107    }
108
109    /// Accepts a connection and returns the client's address as
110    /// an `uds_fork::UnixSocketAddr`.
111    fn accept_unix_addr(&self) -> Result<(Self::Conn, UnixSocketAddr), io::Error>;
112}
113
114impl UnixListenerExt for UnixListener 
115{
116    type Conn = UnixStream;
117
118    fn bind_unix_addr(on: &UnixSocketAddr) -> Result<Self, io::Error> 
119    {
120        let socket = Socket::<SocketStream>::new(false)?;
121        socket.set_unix_local_addr(on)?;
122
123        socket.start_listening()?;
124        
125        return 
126            Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
127    }
128
129    fn accept_unix_addr(&self) -> Result<(Self::Conn, UnixSocketAddr), io::Error> 
130    {
131        let (socket, addr) = Socket::<SocketStream>::accept_from(self, false)?;
132        let conn = 
133                Self::Conn::from(<Socket<SocketStream> as Into<OwnedFd>>::into(socket));
134        
135        return Ok((conn, addr));
136    }
137}
138
139/// Extension trait for `std::os::unix::net::UnixDatagram` and nonblocking equivalents.
140pub trait UnixDatagramExt: AsFd + AsRawFd + FromRawFd 
141{
142    /// Create a socket bound to a path or abstract name.
143    ///
144    /// # Examples
145    ///
146    #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
147    #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
148    /// # use std::os::unix::net::UnixDatagram;
149    /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
150    /// #
151    /// # fn main() -> Result<(), std::io::Error> {
152    /// let addr = UnixSocketAddr::new("@abstract")?;
153    /// let socket = UnixDatagram::bind_unix_addr(&addr)?;
154    /// # let _ = socket.send_to_unix_addr(b"where are you", &addr);
155    /// # Ok(())
156    /// # }
157    /// ```
158    ///
159    /// This is equivalent of:
160    ///
161    /// ```
162    /// # use std::os::unix::net::UnixDatagram;
163    /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
164    /// #
165    /// # fn main() -> Result<(), std::io::Error> {
166    /// # let addr = UnixSocketAddr::new("/tmp/me")?;
167    /// let socket = UnixDatagram::unbound()?;
168    /// socket.bind_to_unix_addr(&addr)?;
169    /// # let _ = std::fs::remove_file("/tmp/me");
170    /// # Ok(())
171    /// # }
172    /// ```
173    fn bind_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error> 
174        where Self: Sized;
175
176    /// Returns the address of this socket, as a type that fully supports abstract addresses.
177    fn local_unix_addr(&self) -> Result<UnixSocketAddr, io::Error> 
178    {
179        get_unix_local_addr(self)
180    }
181
182    /// Returns the address of the connected socket, as a type that fully supports abstract addresses.
183    fn peer_unix_addr(&self) -> Result<UnixSocketAddr, io::Error> 
184    {
185        get_unix_peer_addr(self)
186    }
187
188    /// Creates a path or abstract name for the socket.
189    fn bind_to_unix_addr(&self,  addr: &UnixSocketAddr) -> Result<(), io::Error> 
190    {
191        set_unix_local_addr(self, addr)
192    }
193
194    /// Connects the socket to a path-based or abstract named socket.
195    fn connect_to_unix_addr(&self,  addr: &UnixSocketAddr) -> Result<(), io::Error> 
196    {
197        set_unix_peer_addr(self, addr)
198    }
199
200    /// Sends to the specified address, using an address type that
201    /// supports abstract addresses.
202    ///
203    /// # Examples
204    ///
205    /// Send to an abstract address:
206    ///
207    #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
208    #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
209    /// # use std::os::unix::net::UnixDatagram;
210    /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
211    /// #
212    /// let socket = UnixDatagram::unbound().expect("create datagram socket");
213    /// let _ = socket.send_to_unix_addr(
214    ///     b"Is there anyone there?",
215    ///     &UnixSocketAddr::from_abstract("somewhere").expect("OS supports abstract addresses"),
216    /// );
217    /// ```
218    fn send_to_unix_addr(&self,  datagram: &[u8],  addr: &UnixSocketAddr) -> Result<usize, io::Error> 
219    {
220        let (sockaddr, socklen) = addr.as_raw_general();
221        
222        return 
223            unsafe {
224                cvt_r!(
225                    sendto(
226                        self.as_raw_fd(),
227                        datagram.as_ptr() as *const c_void,
228                        datagram.len(),
229                        MSG_NOSIGNAL,
230                        sockaddr,
231                        socklen,
232                    )
233                )
234                .map(|signed| signed as usize )
235            };
236    }
237    /// Sends a datagram created from multiple segments to the specified address,
238    /// using an address type that supports abstract addresses.
239    ///
240    /// # Examples
241    ///
242    /// Send a datagram with a fixed header:
243    ///
244    /// ```
245    /// # use std::os::unix::net::UnixDatagram;
246    /// # use std::io::IoSlice;
247    /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
248    /// #
249    /// let socket = UnixDatagram::unbound().expect("create datagram socket");
250    /// let to = UnixSocketAddr::new("/var/run/someone.sock").unwrap();
251    /// let msg = [
252    ///     IoSlice::new(b"hello "),
253    ///     IoSlice::new(to.as_pathname().unwrap().to_str().unwrap().as_bytes()),
254    /// ];
255    /// let _ = socket.send_vectored_to_unix_addr(&msg, &to);
256    /// ```
257    fn send_vectored_to_unix_addr(&self,  datagram: &[IoSlice],  addr: &UnixSocketAddr) -> Result<usize, io::Error> 
258    {
259        send_ancillary(self, Some(addr), 0, datagram, Vec::new(), None)
260    }
261
262    /// Receives from any peer, storing its address in a type that exposes
263    /// abstract addresses.
264    ///
265    /// # Examples
266    ///
267    /// Respond to the received datagram, regardsless of where it was sent from:
268    ///
269    /// ```
270    /// use std::os::unix::net::UnixDatagram;
271    /// use uds_fork::{UnixSocketAddr, UnixDatagramExt};
272    ///
273    /// let _ = std::fs::remove_file("/tmp/echo.sock");
274    /// let server = UnixDatagram::bind("/tmp/echo.sock").expect("create server socket");
275    ///
276    /// let client_addr = UnixSocketAddr::new("@echo_client")
277    ///     .or(UnixSocketAddr::new("/tmp/echo_client.sock"))
278    ///     .unwrap();
279    /// let client = UnixDatagram::unbound().expect("create client ocket");
280    /// client.bind_to_unix_addr(&client_addr).expect("create client socket");
281    /// client.connect_to_unix_addr(&UnixSocketAddr::new("/tmp/echo.sock").unwrap())
282    ///     .expect("connect to server");
283    /// client.send(b"hello").expect("send");
284    ///
285    /// let mut buf = [0; 1024];
286    /// let (len, from) = server.recv_from_unix_addr(&mut buf).expect("receive");
287    /// server.send_to_unix_addr(&buf[..len], &from).expect("respond");
288    ///
289    /// let len = client.recv(&mut buf).expect("receive response");
290    /// assert_eq!(&buf[..len], "hello".as_bytes());
291    ///
292    /// let _ = std::fs::remove_file("/tmp/echo.sock");
293    /// if let Some(client_path) = client_addr.as_pathname() {
294    ///     let _ = std::fs::remove_file(client_path);
295    /// }
296    /// ```
297    fn recv_from_unix_addr(&self,  buf: &mut[u8]) -> Result<(usize, UnixSocketAddr), io::Error> 
298    {
299        UnixSocketAddr::new_from_ffi(
300            |addr, len| 
301            {
302                unsafe {
303                    cvt_r!(
304                        recvfrom(
305                            self.as_raw_fd(),
306                            buf.as_ptr() as *mut c_void,
307                            buf.len(),
308                            MSG_NOSIGNAL,
309                            addr,
310                            len,
311                        )
312                    )
313                    .map(|signed| signed as usize )
314                }
315            }
316        )
317    }
318    /// Uses multiple buffers to receive from any peer, storing its address in
319    /// a type that exposes abstract addresses.
320    fn recv_vectored_from_unix_addr(&self,  bufs: &mut[IoSliceMut]) -> Result<(usize, UnixSocketAddr), io::Error> 
321    {
322        let mut addr = UnixSocketAddr::default();
323
324        recv_fds(self, Some(&mut addr), bufs, None)
325            .map(|(bytes, _, _)| (bytes, addr) )
326    }
327    
328    /// Reads the next datagram without removing it from the queue.
329    ///
330    /// # Examples
331    ///
332    /// Discard datagram if it's the wrong protocol:
333    ///
334    /// ```
335    /// # use std::os::unix::net::UnixDatagram;
336    /// # use uds_fork::{UnixSocketAddr, UnixDatagramExt};
337    /// #
338    /// let checker = UnixDatagram::bind("/tmp/checker.sock").expect("create receiver socket");
339    ///
340    /// let client = UnixDatagram::unbound().expect("create client ocket");
341    /// client.send_to(b"hello", "/tmp/checker.sock").expect("send");
342    ///
343    /// let mut header = [0; 4];
344    /// let (len, _from) = checker.peek_from_unix_addr(&mut header).expect("receive");
345    /// if len != 4  ||  header != *b"WTFP" {
346    ///     let _ = checker.recv(&mut header); // discard
347    /// } else {
348    ///     // call function that receives and processes it
349    /// }
350    /// #
351    /// # let _ = std::fs::remove_file("/tmp/checker.sock");
352    /// ```
353    fn peek_from_unix_addr(&self,  buf: &mut[u8]) -> Result<(usize, UnixSocketAddr), io::Error> 
354    {
355        UnixSocketAddr::new_from_ffi(
356            |addr, len| 
357            {
358            unsafe 
359            {
360                cvt_r!(
361                    recvfrom(
362                        self.as_raw_fd(),
363                        buf.as_ptr() as *mut c_void,
364                        buf.len(),
365                        MSG_PEEK | MSG_NOSIGNAL,
366                        addr,
367                        len,
368                    )
369                )
370                .map(|signed| signed as usize )
371            }
372        })
373    }
374
375    /// Uses multiple buffers to read the next datagram without removing it
376    /// from the queue.
377    ///
378    /// # Examples
379    ///
380    #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
381    #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
382    /// use std::os::unix::net::UnixDatagram;
383    /// use std::io::IoSliceMut;
384    /// use uds_fork::{UnixDatagramExt, UnixSocketAddr};
385    ///
386    /// # let _ = std::fs::remove_file("/tmp/datagram_server.sock");
387    /// let server = UnixDatagram::bind("/tmp/datagram_server.sock").unwrap();
388    ///
389    /// // get a random abstract address on Linux
390    /// let client = UnixDatagram::unbound().unwrap();
391    /// client.bind_to_unix_addr(&UnixSocketAddr::new_unspecified()).unwrap();
392    /// client.connect("/tmp/datagram_server.sock").unwrap();
393    /// client.send(b"headerbodybody").unwrap();
394    ///
395    /// let (mut buf_a, mut buf_b) = ([0; 6], [0; 12]);
396    /// let mut vector = [IoSliceMut::new(&mut buf_a), IoSliceMut::new(&mut buf_b)];
397    /// let (bytes, addr) = server.peek_vectored_from_unix_addr(&mut vector).unwrap();
398    /// assert_eq!(addr, client.local_unix_addr().unwrap());
399    /// assert_eq!(bytes, 14);
400    /// assert_eq!(&buf_a, b"header");
401    /// assert_eq!(&buf_b[..8], b"bodybody");
402    /// #
403    /// # std::fs::remove_file("/tmp/datagram_server.sock").unwrap();
404    /// ```
405    fn peek_vectored_from_unix_addr(&self,  bufs: &mut[IoSliceMut]) -> Result<(usize, UnixSocketAddr), io::Error> 
406    {
407        let mut addr = UnixSocketAddr::default();
408
409        recv_ancillary(self,Some(&mut addr),MSG_PEEK | MSG_NOSIGNAL, bufs,&mut[])
410            .map(|(bytes, _)| (bytes, addr) )
411    }
412
413    /// Sends file descriptors along with the datagram, on an unconnected socket.
414    fn send_fds_to(&self, datagram: &[u8], fds: Vec<OwnedFd>, addr: &UnixSocketAddr) -> Result<usize, io::Error> 
415    {
416        send_ancillary(self, Some(addr), 0, &[IoSlice::new(datagram)], fds, None)
417    }
418
419    /// Sends file descriptors along with the datagram, on a connected socket.
420    fn send_fds(&self, datagram: &[u8], fds: Vec<OwnedFd>) -> Result<usize, io::Error> 
421    {
422        send_ancillary(self, None, 0, &[IoSlice::new(datagram)], fds, None)
423    }
424
425    /// Receives file descriptors along with the datagram, on an unconnected socket
426    fn recv_fds_from(&self,  buf: &mut[u8],  fd_buf: &mut Vec<OwnedFd>) -> Result<(usize, usize, UnixSocketAddr), io::Error> 
427    {
428        let mut addr = UnixSocketAddr::default();
429        recv_fds(self, Some(&mut addr), &mut[IoSliceMut::new(buf)], Some(fd_buf))
430            .map(|(bytes, _, fds)| (bytes, fds, addr) )
431    }
432
433    /// Receives file descriptors along with the datagram, on a connected socket
434    fn recv_fds(&self,  buf: &mut[u8],  fd_buf: &mut Vec<OwnedFd>) -> Result<(usize, usize), io::Error> 
435    {
436        recv_fds(self, None, &mut[IoSliceMut::new(buf)], Some(fd_buf))
437            .map(|(bytes, _, fds)| (bytes, fds) )
438    }
439
440    /// Returns the credentials of the process that created a socket pair.
441    ///
442    /// This information is only available on Linux, and only for sockets that
443    /// was created with `pair()` or the underlying `socketpair()`.
444    /// For sockets that have merely been "connected" to an address
445    /// or not connected at all, an error of kind `NotConnected`
446    /// or `InvalidInput` is returned.
447    ///
448    /// The use cases of this function gotta be very narrow:
449    ///
450    /// * It will return the credentials of the current process unless
451    ///   the side of the socket this method is called on was received via
452    ///   FD-passing or inherited from a parent.
453    /// * If it was created by the direct parent process,
454    ///   one might as well use `getppid()` and go from there?
455    /// * A returned pid can be repurposed by the OS before the call returns.
456    /// * uids or groups will be those in effect when the pair was created,
457    ///   and will not reflect changes in privileges.
458    ///
459    /// Despite these limitations, the feature is supported by Linux at least
460    /// (but not macOS or FreeBSD), so might as well expose it.
461    fn initial_pair_credentials(&self) -> Result<ConnCredentials, io::Error> 
462    {
463        peer_credentials(self)
464    }
465    /// Returns the SELinux security context of the process that created a socket pair.
466    ///
467    /// Has the same limitations and gotchas as `initial_pair_credentials()`,
468    /// and will return an error on other OSes than Linux or Android
469    /// or if running under kubernetes.
470    ///
471    /// The default security context is the string `unconfined`.
472    fn initial_pair_selinux_context(&self,  buffer: &mut[u8]) -> Result<usize, io::Error> 
473    {
474        selinux_context(self, buffer)
475    }
476}
477
478impl UnixDatagramExt for UnixDatagram 
479{
480    fn bind_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error> 
481    {
482        return 
483            UnixDatagram::unbound()
484                .map_or_else(
485                    |e| Err(e), 
486                    |socket|
487                        socket
488                            .bind_to_unix_addr(addr)
489                            .map_or_else(
490                                |e| Err(e), 
491                                |_| Ok(socket)
492                            )
493                );
494    }
495}