#![forbid(unsafe_code)]
use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, sys::socket::SockaddrStorage};
use crate::{
cache::UnixVal,
compat::{sockaddr_family, AddressFamily},
cookie::safe_connect,
fd::{has_recv_timeout, SafeOwnedFd},
kernel::net::handle_safe_bind,
path::XPath,
req::UNotifyEventRequest,
unix::unix_path_bytes,
};
pub(crate) fn handle_connect(
fd: SafeOwnedFd,
addr: (SockaddrStorage, SockaddrStorage),
request: &UNotifyEventRequest,
allow_safe_bind: bool,
is_nonblock: bool,
) -> Result<ScmpNotifResp, Errno> {
let (addr, argaddr) = addr;
let req = request.scmpreq;
let is_blocking = if !is_nonblock {
let ignore_restart = has_recv_timeout(&fd)?;
request.cache.add_sys_block(req, ignore_restart)?;
true
} else {
false
};
let result = safe_connect(&fd, &addr);
if is_blocking {
request.cache.del_sys_block(req.id)?;
}
if result.is_ok() {
if allow_safe_bind
&& matches!(
sockaddr_family(&addr),
AddressFamily::Inet | AddressFamily::Inet6
)
{
let _ = handle_safe_bind(request, &fd);
} else if sockaddr_family(&addr) == AddressFamily::Unix {
let unix_peer = argaddr.as_unix_addr().filter(|u| u.path().is_some());
let (ddev, dino) = unix_peer
.and_then(unix_path_bytes)
.map(XPath::from_bytes)
.and_then(|path| request.lookup_unix_vfs_id(path).ok())
.map_or((None, None), |(dev, ino)| (Some(dev), Some(ino)));
let mut unix_val = UnixVal {
peer: unix_peer.copied(),
..UnixVal::default()
};
if let (Some(dev), Some(ino)) = (ddev, dino) {
if unix_val.dest.try_reserve(1).is_ok() {
unix_val.dest.push((dev, ino));
}
}
let _ = request.add_unix(&fd, request.scmpreq.pid(), unix_val);
}
}
result.map(|_| request.return_syscall(0))
}