uds-fork 0.6.1

A unix domain socket crate that supports abstract addresses, fd-passing, seqpacket and windows unix stream sockets.
Documentation

uds

[!IMPORTANT]
I am not original author. A GitHub and Crates are links to original crate. This crate is forked!

[!IMPORTANT]
Since an author is not responding on issues at his github page, I decided to fork the crate.

A unix domain sockets Rust library that supports abstract addresses, fd-passing, SOCK_SEQPACKET sockets SOCK_STREAM for the Windows and more.

A AF_UNIX SOCK_STREAM is implemented for Windows in windows_unixstream.rs as an experiment. WSA veriosn 2.2 is required.

When possible, features are implemented via extension traits for std::os::unix::net types (and optionally mio's uds types) instead of exposing new structs. The only new socket structs this crate exposes are those for seqpacket sockets.

Ancillary credentials and timestamps are not yet supported.

Changelog

  • Fixed release date of previous version
  • Added more info and comments.
  • The crate's addr.rs is used as dependancy, so this crate can be used on Windows to utilize the UnixSocketAddr.
  • Added experimental support for unixstream in Windows i.e AF_UNIX SOCK_STREAM.
  • Added missing implementation of Deref for NonblockingUnixSeqpacketListener.
  • Fixed address already in use problems in docs.
  • Added from OwnedFd and from RawFd and from Self to OwnedFd. Functions which performs conversion from OwnedFd to Self perform check of the socket type.
  • NonblockingUnixSeqpacketListener and NonblockingUnixSeqpacketConn is now have inside an instance of UnixSeqpacketConn and UnixSeqpacketListener respectivly which is set to non-blocking.
  • Reorganised some functions
  • Fixed compilation errors on OpenBSD
  • Fixed one test problem (related to what kernel returns)
  • Fixed compilation errors on FreeBSD.
  • ! FreeBSD 15 demonstrates strange behaviour with message trancation and empty messages which is not compatiable Linux and FreeBSD 14.
  • send_ancillary() memory managment modified
  • A crate was forked due to stale status.
  • Moved to Rust 2024
  • Added OwnedFd and BorrowedFd to comply with Rust specifications.
  • Removed MIO and Tokio. Use AsyncFD and implement MIO for the type youself. (Maybe MIO will be readded later).

Examples

(only runs sucessfully on Linux)

extern crate uds_fork;

use std::os::{unix::net::UnixDatagram, fd::OwnedFd};

let addr = uds_fork::UnixSocketAddr::from_abstract(b"not a file!")
    .expect("create abstract socket address");
let listener = uds_fork::UnixSeqpacketListener::bind_unix_addr(&addr)
    .expect("create seqpacket listener");

let client = uds_fork::UnixSeqpacketConn::connect_unix_addr(&addr)
    .expect("connect to listener");
let (a, b) = UnixDatagram::pair().expect("create datagram socket pair");
let (aa, _bb) = UnixDatagram::pair().expect("create datagram socket pair");

client.send_fds(b"Here I come", vec![OwnedFd::from(a), OwnedFd::from(b), OwnedFd::from(aa)])
    .expect("send stdin, stdout and stderr");

let (server_side, _) = listener.accept_unix_addr()
    .expect("accept connection");
let creds: uds_fork::ConnCredentials = server_side.initial_peer_credentials()
    .expect("get peer credentials");
if creds.euid() == 0 {
    let mut fd_buf = Vec::with_capacity(3);
    let (_, _, fds) = server_side.recv_fds(&mut[0u8; 1], &mut fd_buf
        ).expect("receive with fd capacity");
    if fds == 3 {
        /* do something with the file descriptors */
    }
    /* remember to close the file descripts */
} else {
    server_side.send(b"go away!\n").expect("send response");
}

(only runs sucessfully on Windows)

extern crate uds_fork;
use uds_fork::{RecvFlags, WindowsUnixListener, WindowsUnixStream};

  let path = "server3.sock";
  rem_sock(path);

  let wul = WindowsUnixListener::bind(path).unwrap();

  let client = WindowsUnixStream::connect(path).unwrap();

  let (accp_client, rem_addr) = wul.accept_unix_addr().unwrap();

  println!("accepted connection: {}", rem_addr);
  assert_eq!(rem_addr.to_string().as_str(), "unnamed");

  let sa_fam = uds_fork::get_socket_family(&accp_client).unwrap();
  let sa_type = uds_fork::get_socket_type(&accp_client).unwrap();
  assert_eq!(sa_fam, AF_UNIX);
  assert_eq!(sa_type, SOCK_STREAM);

  println!("{} {}", sa_fam, sa_type);

  let data: [u8; 4] = [1,2,3,4];

  let srv_n = client.send(&data).unwrap();
  assert_eq!(srv_n, data.len());

  let mut rcv_data = [0_u8; 10];
  let rcv_n = accp_client.recv(&mut rcv_data).unwrap();
  assert_eq!(srv_n, rcv_n);
  assert_eq!(&data, &rcv_data[..srv_n]);


  // accp_client -> client
    let data: [u8; 4] = [5,6,7,8];

  let srv_n = accp_client.send(&data).unwrap();
  assert_eq!(srv_n, data.len());

  let mut rcv_data = [0_u8; 10];
  let rcv_n = client.recv(&mut rcv_data).unwrap();
  assert_eq!(srv_n, rcv_n);
  assert_eq!(&data, &rcv_data[..srv_n]);

  rem_sock(path);

Using with MIO

The MIO crate depedancy was removed because it can be implemented for the socket types of this crate by the programmer.

impl  event::Source  for NonblockingUnixSeqpacketConn
{
  fn register(&mut self,  registry: &Registry,  token: Token,  interest: Interest )
  -> Result<(), io::Error> 
  {
      unix::SourceFd(&self.fd).register(registry, token, interest)
  }
  
  fn reregister(&mut self,  registry: &Registry,  token: Token,  interest: Interest)
  -> Result<(), io::Error> 
  {
      unix::SourceFd(&self.fd).reregister(registry, token, interest)
  }
  
  fn deregister(&mut self,  registry: &Registry) -> Result<(), io::Error> 
  {
      unix::SourceFd(&self.fd).deregister(registry)
  }
}

Portability

macOS doesn't support SOCK_SEQPACKET sockets, and abstract socket addresses is Linux-only, so if you don't want to bother with supporting non-portable features you are probably better off only using what std or mio provides. If you're writing a datagram server though, using std or mio means you can't respond to abstract adresses, forcing clients to use path addresses and deal with cleaning up the socket file after themselves.

Even when all operating systems you care about supports something, they might behave differently:
On Linux file descriptors are cloned when they are sent, but macOS and the BSDs first clones them when they are received. This means that if a FD is closed before the peer receives it you have a problem.
Also, some OSes might return the original file descriptor without cloning it if it's received within the same process as it was sent from. (DragonFly BSD, possibly macOS and maybe FreeBSD).

Linux macOS FreeBSD OpenBSD DragonFly BSD NetBSD Illumos Windows
Seqpacket Yes N/A Yes Yes Yes Yes N/A N/A
fd-passing Yes Yes Yes Yes Yes Yes No No
abstract addresses Yes N/A N/A N/A N/A N/A N/A N/A
unix_stream_win N/A N/A N/A N/A N/A N/A N/A Yes
Tested? Manually* Manually* Manually* Manually* Manually* Manually* Manually* Manually*

*: Not tested since v0.2.6. (but (cross)checked on CI.)

Other OSes

  • FreeBSD 15 (from version to version) behaves differently on msg truncation and sending empty messages.
  • Android: I haven't tested on it, but I assume there are no differences from regular Linux.
  • Windows 10: While it added some unix socket features, Windows support is not a priority. (PRs are welcome though).
  • Solaris: Treated identically as Illumos. mio 0.8 doesn't support it.
  • Windows: UnixSocketAddr and WindowsUnixStream and WindowsUnixListener only!

Minimum Rust version

The minimum Rust version is 1.63.
Older versions might work, but might break in a minor release.

unsafe usage

This crate calls many C functions, which are all unsafe (even ones as simple as socket()). The public interface complies with Rust's FD managment recomendations.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.