use crate::*;
use nix::sys::{
socket::{
AddressFamily, MsgFlags, SockFlag, SockType, UnixAddr, bind, recvfrom,
sendto, setsockopt, socket, sockopt,
},
time::{TimeVal, TimeValLike},
};
use std::os::{fd::OwnedFd, unix::io::AsRawFd};
pub struct UauSock {
fd: OwnedFd,
sa: UnixAddr,
}
impl UauSock {
pub fn new(addr: &[u8], recv_timeout: Option<i64>) -> Result<Self> {
let fd = socket(
AddressFamily::Unix,
SockType::Datagram,
SockFlag::empty(),
None,
)
.c(d!())?;
setsockopt(&fd, sockopt::ReuseAddr, &true).c(d!())?;
if let Some(to) = recv_timeout {
setsockopt(
&fd,
sockopt::ReceiveTimeout,
&TimeVal::milliseconds(to),
)
.c(d!())?;
}
let sa = UnixAddr::new_abstract(addr).c(d!())?;
bind(fd.as_raw_fd(), &sa).c(d!())?;
Ok(UauSock { fd, sa })
}
#[inline(always)]
pub fn create(recv_timeout: Option<i64>) -> Result<Self> {
let addr = (ts!() as u32 ^ rand::random::<u32>()).to_ne_bytes();
Self::new(&addr, recv_timeout).c(d!())
}
#[inline(always)]
pub fn addr(&self) -> &UnixAddr {
&self.sa
}
#[inline(always)]
pub fn send(&self, msg: &[u8], peeraddr: &UnixAddr) -> Result<()> {
sendto(self.fd.as_raw_fd(), msg, peeraddr, MsgFlags::empty())
.c(d!())
.map(|_| ())
}
#[inline(always)]
pub fn recv_buf<const N: usize>(&self) -> Result<(Vec<u8>, UnixAddr)> {
let mut buf = vec![0u8; N + 1];
let (n, peer) = self.recv(&mut buf).c(d!())?;
ensure!(
n <= N,
"datagram larger than the {N}-byte buffer, choose a larger `N`"
);
buf.truncate(n);
Ok((buf, peer))
}
#[inline(always)]
pub fn recvonly_buf<const N: usize>(&self) -> Result<Vec<u8>> {
self.recv_buf::<N>().map(|(b, _)| b)
}
#[inline(always)]
pub fn recv(&self, buf: &mut [u8]) -> Result<(usize, UnixAddr)> {
match recvfrom::<UnixAddr>(self.fd.as_raw_fd(), buf) {
Ok((n, Some(peer))) => Ok((n, peer)),
Err(e) => Err(eg!(e)),
_ => Err(eg!("peer address is unknown")),
}
}
pub fn addr_to_sock(addr: &[u8]) -> Result<UnixAddr> {
UnixAddr::new_abstract(addr).c(d!())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn t_send_recv() {
let sender = pnk!(UauSock::create(None));
let receiver = pnk!(UauSock::create(None));
for _ in 0..4 {
pnk!(
sender.send(&987654321_u32.to_ne_bytes()[..], receiver.addr())
);
assert_eq!(
&987654321_u32.to_ne_bytes()[..],
&pnk!(receiver.recvonly_buf::<64>())
);
}
}
#[test]
fn t_recv_buf_rejects_oversized_datagram() {
let sender = pnk!(UauSock::create(None));
let receiver = pnk!(UauSock::create(None));
pnk!(sender.send(&[0u8; 8][..], receiver.addr()));
assert!(receiver.recvonly_buf::<4>().is_err());
pnk!(sender.send(&[7u8; 4][..], receiver.addr()));
assert_eq!(&[7u8; 4][..], &pnk!(receiver.recvonly_buf::<4>()));
}
#[test]
fn t_send_recv_const_generic() {
let sender = pnk!(UauSock::create(None));
let receiver = pnk!(UauSock::create(None));
pnk!(sender.send(&987654321_u32.to_ne_bytes()[..], receiver.addr()));
assert_eq!(
&987654321_u32.to_ne_bytes()[..],
&pnk!(receiver.recvonly_buf::<64>())
);
pnk!(sender.send(&987654321_u32.to_ne_bytes()[..], receiver.addr()));
assert_eq!(
&987654321_u32.to_ne_bytes()[..],
&pnk!(receiver.recvonly_buf::<256>())
);
}
}