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}