use std::io;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use std::os::unix::net::{UnixDatagram, UnixListener};
use std::path::Path;
use crate::io::source::Kind;
use crate::io::{Handle, sys};
pub struct UnixSocket {
socket: OwnedFd,
datagrams: bool,
}
impl UnixSocket {
pub fn new_stream() -> io::Result<UnixSocket> {
Self::new(false)
}
pub fn new_datagram() -> io::Result<UnixSocket> {
Self::new(true)
}
fn new(datagrams: bool) -> io::Result<UnixSocket> {
let ty = if datagrams {
libc::SOCK_DGRAM
} else {
libc::SOCK_STREAM
};
Ok(UnixSocket {
socket: sys::socket(libc::AF_UNIX, ty)?,
datagrams,
})
}
pub fn bind(&self, path: impl AsRef<Path>) -> io::Result<()> {
sys::bind_unix(
self.socket.as_raw_fd(),
&sys::UnixAddr::path(path.as_ref())?,
)
}
pub fn listen(self, backlog: u32) -> io::Result<super::UnixListener> {
if self.datagrams {
return Err(wrong_kind("a socket of datagrams cannot listen"));
}
sys::listen(self.socket.as_raw_fd(), backlog)?;
Ok(Handle::from_nonblocking(
UnixListener::from(self.socket),
Kind::UnixListener,
))
}
pub async fn connect(self, path: impl AsRef<Path>) -> io::Result<super::UnixStream> {
if self.datagrams {
return Err(wrong_kind(
"a socket of datagrams connects as a datagram socket",
));
}
super::unix::connect(self.socket, &sys::UnixAddr::path(path.as_ref())?).await
}
pub fn datagram(self) -> io::Result<super::UnixDatagram> {
if !self.datagrams {
return Err(wrong_kind("a socket of streams is not a datagram socket"));
}
Ok(Handle::from_nonblocking(
UnixDatagram::from(self.socket),
Kind::UnixDatagram,
))
}
}
fn wrong_kind(what: &'static str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, what)
}
impl core::fmt::Debug for UnixSocket {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("UnixSocket")
.field("fd", &self.socket.as_raw_fd())
.field("datagrams", &self.datagrams)
.finish()
}
}
impl AsRawFd for UnixSocket {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl AsFd for UnixSocket {
fn as_fd(&self) -> BorrowedFd<'_> {
self.socket.as_fd()
}
}
impl IntoRawFd for UnixSocket {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}
impl FromRawFd for UnixSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
let socket = unsafe { OwnedFd::from_raw_fd(fd) };
let mut ty: libc::c_int = 0;
let mut len = size_of::<libc::c_int>() as libc::socklen_t;
let r = unsafe {
libc::getsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_TYPE,
core::ptr::from_mut(&mut ty).cast(),
&mut len,
)
};
UnixSocket {
socket,
datagrams: r == 0 && ty == libc::SOCK_DGRAM,
}
}
}