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#[cfg(feature = "mio")]
94impl UnixStreamExt for mio::net::UnixStream
95{
96 fn connect_to_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error>
97 {
98 let socket = Socket::<SocketStream>::new(true)?;
99 socket.set_unix_peer_addr(addr)?;
100
101 return Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
102 }
103
104 fn connect_from_to_unix_addr(from: &UnixSocketAddr, to: &UnixSocketAddr) -> Result<Self, io::Error>
105 {
106 let socket = Socket::<SocketStream>::new(true)?;
107 socket.set_unix_local_addr(from)?;
108 socket.set_unix_peer_addr(to)?;
109
110 return Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
111 }
112}
113
114/// Extension trait for using [`UnixSocketAddr`](struct.UnixSocketAddr.html) with `UnixListener` types.
115pub trait UnixListenerExt: AsFd + AsRawFd + FromRawFd
116{
117 /// The type represeting the stream connection returned by `accept_unix_addr()`.
118 type Conn: FromRawFd;
119
120 /// Creates a socket bound to a `UnixSocketAddr` and starts listening on it.
121 fn bind_unix_addr(on: &UnixSocketAddr) -> Result<Self, io::Error>
122 where Self: Sized;
123
124 /// Returns the address this socket is listening on.
125 fn local_unix_addr(&self) -> Result<UnixSocketAddr, io::Error>
126 {
127 get_unix_local_addr(self)
128 }
129
130 /// Accepts a connection and returns the client's address as
131 /// an `uds_fork::UnixSocketAddr`.
132 fn accept_unix_addr(&self) -> Result<(Self::Conn, UnixSocketAddr), io::Error>;
133}
134
135impl UnixListenerExt for UnixListener
136{
137 type Conn = UnixStream;
138
139 fn bind_unix_addr(on: &UnixSocketAddr) -> Result<Self, io::Error>
140 {
141 let socket = Socket::<SocketStream>::new(false)?;
142 socket.set_unix_local_addr(on)?;
143
144 socket.start_listening()?;
145
146 return
147 Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
148 }
149
150 fn accept_unix_addr(&self) -> Result<(Self::Conn, UnixSocketAddr), io::Error>
151 {
152 let (socket, addr) = Socket::<SocketStream>::accept_from(self, false)?;
153 let conn =
154 Self::Conn::from(<Socket<SocketStream> as Into<OwnedFd>>::into(socket));
155
156 return Ok((conn, addr));
157 }
158}
159
160
161#[cfg(feature = "mio")]
162impl UnixListenerExt for mio::net::UnixListener
163{
164 type Conn = mio::net::UnixStream;
165
166 fn bind_unix_addr(on: &UnixSocketAddr) -> Result<Self, io::Error>
167 {
168 let socket = Socket::<SocketStream>::new(true)?;
169 socket.set_unix_local_addr(on)?;
170
171 socket.start_listening()?;
172
173 return
174 Ok(Self::from( <Socket<SocketStream> as Into<OwnedFd>>::into(socket)));
175 }
176
177 fn accept_unix_addr(&self) -> Result<(Self::Conn, UnixSocketAddr), io::Error>
178 {
179 let (socket, addr) = Socket::<SocketStream>::accept_from(self, true)?;
180
181 let conn =
182 Self::Conn::from(<Socket<SocketStream> as Into<OwnedFd>>::into(socket));
183
184 Ok((conn, addr))
185 }
186}
187
188/// Extension trait for `std::os::unix::net::UnixDatagram` and nonblocking equivalents.
189pub trait UnixDatagramExt: AsFd + AsRawFd + FromRawFd
190{
191 /// Create a socket bound to a path or abstract name.
192 ///
193 /// # Examples
194 ///
195 #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
196 #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
197 /// # use std::os::unix::net::UnixDatagram;
198 /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
199 /// #
200 /// # fn main() -> Result<(), std::io::Error> {
201 /// let addr = UnixSocketAddr::new("@abstract")?;
202 /// let socket = UnixDatagram::bind_unix_addr(&addr)?;
203 /// # let _ = socket.send_to_unix_addr(b"where are you", &addr);
204 /// # Ok(())
205 /// # }
206 /// ```
207 ///
208 /// This is equivalent of:
209 ///
210 /// ```
211 /// # use std::os::unix::net::UnixDatagram;
212 /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
213 /// #
214 /// # fn main() -> Result<(), std::io::Error> {
215 /// # let addr = UnixSocketAddr::new("/tmp/me")?;
216 /// let socket = UnixDatagram::unbound()?;
217 /// socket.bind_to_unix_addr(&addr)?;
218 /// # let _ = std::fs::remove_file("/tmp/me");
219 /// # Ok(())
220 /// # }
221 /// ```
222 fn bind_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error>
223 where Self: Sized;
224
225 /// Returns the address of this socket, as a type that fully supports abstract addresses.
226 fn local_unix_addr(&self) -> Result<UnixSocketAddr, io::Error>
227 {
228 get_unix_local_addr(self)
229 }
230
231 /// Returns the address of the connected socket, as a type that fully supports abstract addresses.
232 fn peer_unix_addr(&self) -> Result<UnixSocketAddr, io::Error>
233 {
234 get_unix_peer_addr(self)
235 }
236
237 /// Creates a path or abstract name for the socket.
238 fn bind_to_unix_addr(&self, addr: &UnixSocketAddr) -> Result<(), io::Error>
239 {
240 set_unix_local_addr(self, addr)
241 }
242
243 /// Connects the socket to a path-based or abstract named socket.
244 fn connect_to_unix_addr(&self, addr: &UnixSocketAddr) -> Result<(), io::Error>
245 {
246 set_unix_peer_addr(self, addr)
247 }
248
249 /// Sends to the specified address, using an address type that
250 /// supports abstract addresses.
251 ///
252 /// # Examples
253 ///
254 /// Send to an abstract address:
255 ///
256 #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
257 #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
258 /// # use std::os::unix::net::UnixDatagram;
259 /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
260 /// #
261 /// let socket = UnixDatagram::unbound().expect("create datagram socket");
262 /// let _ = socket.send_to_unix_addr(
263 /// b"Is there anyone there?",
264 /// &UnixSocketAddr::from_abstract("somewhere").expect("OS supports abstract addresses"),
265 /// );
266 /// ```
267 fn send_to_unix_addr(&self, datagram: &[u8], addr: &UnixSocketAddr) -> Result<usize, io::Error>
268 {
269 let (sockaddr, socklen) = addr.as_raw_general();
270
271 return
272 unsafe {
273 cvt_r!(
274 sendto(
275 self.as_raw_fd(),
276 datagram.as_ptr() as *const c_void,
277 datagram.len(),
278 MSG_NOSIGNAL,
279 sockaddr,
280 socklen,
281 )
282 )
283 .map(|signed| signed as usize )
284 };
285 }
286 /// Sends a datagram created from multiple segments to the specified address,
287 /// using an address type that supports abstract addresses.
288 ///
289 /// # Examples
290 ///
291 /// Send a datagram with a fixed header:
292 ///
293 /// ```
294 /// # use std::os::unix::net::UnixDatagram;
295 /// # use std::io::IoSlice;
296 /// # use uds_fork::{UnixDatagramExt, UnixSocketAddr};
297 /// #
298 /// let socket = UnixDatagram::unbound().expect("create datagram socket");
299 /// let to = UnixSocketAddr::new("/var/run/someone.sock").unwrap();
300 /// let msg = [
301 /// IoSlice::new(b"hello "),
302 /// IoSlice::new(to.as_pathname().unwrap().to_str().unwrap().as_bytes()),
303 /// ];
304 /// let _ = socket.send_vectored_to_unix_addr(&msg, &to);
305 /// ```
306 fn send_vectored_to_unix_addr(&self, datagram: &[IoSlice], addr: &UnixSocketAddr) -> Result<usize, io::Error>
307 {
308 send_ancillary(self, Some(addr), 0, datagram, Vec::new(), None)
309 }
310
311 /// Receives from any peer, storing its address in a type that exposes
312 /// abstract addresses.
313 ///
314 /// # Examples
315 ///
316 /// Respond to the received datagram, regardsless of where it was sent from:
317 ///
318 /// ```
319 /// use std::os::unix::net::UnixDatagram;
320 /// use uds_fork::{UnixSocketAddr, UnixDatagramExt};
321 ///
322 /// let dir = tempfile::tempdir().unwrap();
323 /// let mut file_path = dir.path().join("echo.sock");
324 ///
325 /// let server = UnixDatagram::bind(&file_path).expect("create server socket");
326 ///
327 /// let mut cli_file_path = dir.path().join("echo_client.sock");
328 ///
329 /// let client_addr = UnixSocketAddr::new("@echo_client")
330 /// .or(UnixSocketAddr::new(cli_file_path.as_os_str().to_str().unwrap()))
331 /// .unwrap();
332 /// let client = UnixDatagram::unbound().expect("create client ocket");
333 /// client.bind_to_unix_addr(&client_addr).expect("create client socket");
334 /// client.connect_to_unix_addr(&UnixSocketAddr::new(file_path.as_os_str().to_str().unwrap()).unwrap())
335 /// .expect("connect to server");
336 /// client.send(b"hello").expect("send");
337 ///
338 /// let mut buf = [0; 1024];
339 /// let (len, from) = server.recv_from_unix_addr(&mut buf).expect("receive");
340 /// server.send_to_unix_addr(&buf[..len], &from).expect("respond");
341 ///
342 /// let len = client.recv(&mut buf).expect("receive response");
343 /// assert_eq!(&buf[..len], "hello".as_bytes());
344 ///
345 /// let _ = std::fs::remove_file(&file_path);
346 /// if let Some(client_path) = client_addr.as_pathname() {
347 /// let _ = std::fs::remove_file(client_path);
348 /// }
349 /// ```
350 fn recv_from_unix_addr(&self, buf: &mut[u8]) -> Result<(usize, UnixSocketAddr), io::Error>
351 {
352 UnixSocketAddr::new_from_ffi(
353 |addr, len|
354 {
355 unsafe {
356 cvt_r!(
357 recvfrom(
358 self.as_raw_fd(),
359 buf.as_ptr() as *mut c_void,
360 buf.len(),
361 MSG_NOSIGNAL,
362 addr,
363 len,
364 )
365 )
366 .map(|signed| signed as usize )
367 }
368 }
369 )
370 }
371 /// Uses multiple buffers to receive from any peer, storing its address in
372 /// a type that exposes abstract addresses.
373 fn recv_vectored_from_unix_addr(&self, bufs: &mut[IoSliceMut]) -> Result<(usize, UnixSocketAddr), io::Error>
374 {
375 let mut addr = UnixSocketAddr::default();
376
377 recv_fds(self, Some(&mut addr), bufs, None)
378 .map(|(bytes, _, _)| (bytes, addr) )
379 }
380
381 /// Reads the next datagram without removing it from the queue.
382 ///
383 /// # Examples
384 ///
385 /// Discard datagram if it's the wrong protocol:
386 ///
387 /// ```
388 /// # use std::os::unix::net::UnixDatagram;
389 /// # use uds_fork::{UnixSocketAddr, UnixDatagramExt};
390 /// #
391 /// let checker = UnixDatagram::bind("/tmp/checker.sock").expect("create receiver socket");
392 ///
393 /// let client = UnixDatagram::unbound().expect("create client ocket");
394 /// client.send_to(b"hello", "/tmp/checker.sock").expect("send");
395 ///
396 /// let mut header = [0; 4];
397 /// let (len, _from) = checker.peek_from_unix_addr(&mut header).expect("receive");
398 /// if len != 4 || header != *b"WTFP" {
399 /// let _ = checker.recv(&mut header); // discard
400 /// } else {
401 /// // call function that receives and processes it
402 /// }
403 /// #
404 /// # let _ = std::fs::remove_file("/tmp/checker.sock");
405 /// ```
406 fn peek_from_unix_addr(&self, buf: &mut[u8]) -> Result<(usize, UnixSocketAddr), io::Error>
407 {
408 UnixSocketAddr::new_from_ffi(
409 |addr, len|
410 {
411 unsafe
412 {
413 cvt_r!(
414 recvfrom(
415 self.as_raw_fd(),
416 buf.as_ptr() as *mut c_void,
417 buf.len(),
418 MSG_PEEK | MSG_NOSIGNAL,
419 addr,
420 len,
421 )
422 )
423 .map(|signed| signed as usize )
424 }
425 })
426 }
427
428 /// Uses multiple buffers to read the next datagram without removing it
429 /// from the queue.
430 ///
431 /// # Examples
432 ///
433 #[cfg_attr(any(target_os="linux", target_os="android"), doc="```")]
434 #[cfg_attr(not(any(target_os="linux", target_os="android")), doc="```no_run")]
435 /// use std::os::unix::net::UnixDatagram;
436 /// use std::io::IoSliceMut;
437 /// use tempfile::TempDir;
438 /// use uds_fork::{UnixDatagramExt, UnixSocketAddr};
439 ///
440 /// let dir = tempfile::tempdir().unwrap();
441 /// let mut path = dir.path().join("datagram_server.sock");
442 ///
443 /// let server = UnixDatagram::bind(&path).unwrap();
444 ///
445 /// // get a random abstract address on Linux
446 /// let client = UnixDatagram::unbound().unwrap();
447 /// client.bind_to_unix_addr(&UnixSocketAddr::new_unspecified()).unwrap();
448 /// client.connect(&path).unwrap();
449 /// client.send(b"headerbodybody").unwrap();
450 ///
451 /// let (mut buf_a, mut buf_b) = ([0; 6], [0; 12]);
452 /// let mut vector = [IoSliceMut::new(&mut buf_a), IoSliceMut::new(&mut buf_b)];
453 /// let (bytes, addr) = server.peek_vectored_from_unix_addr(&mut vector).unwrap();
454 /// assert_eq!(addr, client.local_unix_addr().unwrap());
455 /// assert_eq!(bytes, 14);
456 /// assert_eq!(&buf_a, b"header");
457 /// assert_eq!(&buf_b[..8], b"bodybody");
458 /// #
459 /// ```
460 fn peek_vectored_from_unix_addr(&self, bufs: &mut[IoSliceMut]) -> Result<(usize, UnixSocketAddr), io::Error>
461 {
462 let mut addr = UnixSocketAddr::default();
463
464 recv_ancillary(self,Some(&mut addr),MSG_PEEK | MSG_NOSIGNAL, bufs,&mut[])
465 .map(|(bytes, _)| (bytes, addr) )
466 }
467
468 /// Sends file descriptors along with the datagram, on an unconnected socket.
469 fn send_fds_to(&self, datagram: &[u8], fds: Vec<OwnedFd>, addr: &UnixSocketAddr) -> Result<usize, io::Error>
470 {
471 send_ancillary(self, Some(addr), 0, &[IoSlice::new(datagram)], fds, None)
472 }
473
474 /// Sends file descriptors along with the datagram, on a connected socket.
475 fn send_fds(&self, datagram: &[u8], fds: Vec<OwnedFd>) -> Result<usize, io::Error>
476 {
477 send_ancillary(self, None, 0, &[IoSlice::new(datagram)], fds, None)
478 }
479
480 /// Receives file descriptors along with the datagram, on an unconnected socket
481 fn recv_fds_from(&self, buf: &mut[u8], fd_buf: &mut Vec<OwnedFd>) -> Result<(usize, usize, UnixSocketAddr), io::Error>
482 {
483 let mut addr = UnixSocketAddr::default();
484 recv_fds(self, Some(&mut addr), &mut[IoSliceMut::new(buf)], Some(fd_buf))
485 .map(|(bytes, _, fds)| (bytes, fds, addr) )
486 }
487
488 /// Receives file descriptors along with the datagram, on a connected socket
489 fn recv_fds(&self, buf: &mut[u8], fd_buf: &mut Vec<OwnedFd>) -> Result<(usize, usize), io::Error>
490 {
491 recv_fds(self, None, &mut[IoSliceMut::new(buf)], Some(fd_buf))
492 .map(|(bytes, _, fds)| (bytes, fds) )
493 }
494
495 /// Returns the credentials of the process that created a socket pair.
496 ///
497 /// This information is only available on Linux, and only for sockets that
498 /// was created with `pair()` or the underlying `socketpair()`.
499 /// For sockets that have merely been "connected" to an address
500 /// or not connected at all, an error of kind `NotConnected`
501 /// or `InvalidInput` is returned.
502 ///
503 /// The use cases of this function gotta be very narrow:
504 ///
505 /// * It will return the credentials of the current process unless
506 /// the side of the socket this method is called on was received via
507 /// FD-passing or inherited from a parent.
508 /// * If it was created by the direct parent process,
509 /// one might as well use `getppid()` and go from there?
510 /// * A returned pid can be repurposed by the OS before the call returns.
511 /// * uids or groups will be those in effect when the pair was created,
512 /// and will not reflect changes in privileges.
513 ///
514 /// Despite these limitations, the feature is supported by Linux at least
515 /// (but not macOS or FreeBSD), so might as well expose it.
516 fn initial_pair_credentials(&self) -> Result<ConnCredentials, io::Error>
517 {
518 peer_credentials(self)
519 }
520 /// Returns the SELinux security context of the process that created a socket pair.
521 ///
522 /// Has the same limitations and gotchas as `initial_pair_credentials()`,
523 /// and will return an error on other OSes than Linux or Android
524 /// or if running under kubernetes.
525 ///
526 /// The default security context is the string `unconfined`.
527 fn initial_pair_selinux_context(&self, buffer: &mut[u8]) -> Result<usize, io::Error>
528 {
529 selinux_context(self, buffer)
530 }
531}
532
533impl UnixDatagramExt for UnixDatagram
534{
535 fn bind_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error>
536 {
537 return
538 UnixDatagram::unbound()
539 .map_or_else(
540 |e| Err(e),
541 |socket|
542 socket
543 .bind_to_unix_addr(addr)
544 .map_or_else(
545 |e| Err(e),
546 |_| Ok(socket)
547 )
548 );
549 }
550}
551
552#[cfg(feature = "mio")]
553impl UnixDatagramExt for mio::net::UnixDatagram
554{
555 fn bind_unix_addr(addr: &UnixSocketAddr) -> Result<Self, io::Error>
556 {
557 return
558 mio::net::UnixDatagram
559 ::unbound()
560 .map_or_else(
561 |e| Err(e),
562 |socket|
563 socket
564 .bind_to_unix_addr(addr)
565 .map_or_else(
566 |e| Err(e),
567 |_| Ok(socket)
568 )
569 );
570 }
571}