use linux_api::errno::Errno;
use linux_api::fcntl::DescriptorFlags;
use linux_api::socket::Shutdown;
use log::*;
use nix::sys::socket::SockFlag;
use shadow_shim_helper_rs::syscall_types::ForeignPtr;
use crate::host::descriptor::descriptor_table::DescriptorHandle;
use crate::host::descriptor::socket::inet::legacy_tcp::LegacyTcpSocket;
use crate::host::descriptor::socket::inet::tcp::TcpSocket;
use crate::host::descriptor::socket::inet::udp::UdpSocket;
use crate::host::descriptor::socket::inet::InetSocket;
use crate::host::descriptor::socket::netlink::{NetlinkFamily, NetlinkSocket, NetlinkSocketType};
use crate::host::descriptor::socket::unix::{UnixSocket, UnixSocketType};
use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs, Socket};
use crate::host::descriptor::{CompatFile, Descriptor, File, FileState, FileStatus, OpenFile};
use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
use crate::host::syscall::io::{self, IoVec};
use crate::host::syscall::type_formatting::{SyscallBufferArg, SyscallSockAddrArg};
use crate::host::syscall::types::ForeignArrayPtr;
use crate::host::syscall::types::SyscallError;
use crate::utility::callback_queue::CallbackQueue;
use crate::utility::sockaddr::SockaddrStorage;
impl SyscallHandler {
log_syscall!(
socket,
std::ffi::c_int,
linux_api::socket::AddressFamily,
std::ffi::c_int,
std::ffi::c_int,
);
pub fn socket(
ctx: &mut SyscallContext,
domain: std::ffi::c_int,
socket_type: std::ffi::c_int,
protocol: std::ffi::c_int,
) -> Result<DescriptorHandle, Errno> {
let flags = socket_type & (libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC);
let socket_type = socket_type & !flags;
let mut file_flags = FileStatus::empty();
let mut descriptor_flags = DescriptorFlags::empty();
if flags & libc::SOCK_NONBLOCK != 0 {
file_flags.insert(FileStatus::NONBLOCK);
}
if flags & libc::SOCK_CLOEXEC != 0 {
descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC);
}
let socket = match domain {
libc::AF_UNIX => {
let socket_type = match UnixSocketType::try_from(socket_type) {
Ok(x) => x,
Err(e) => {
warn!("{}", e);
return Err(Errno::EPROTONOSUPPORT);
}
};
if protocol != 0 {
warn!(
"Unsupported socket protocol {}, we only support default protocol 0",
protocol
);
return Err(Errno::EPROTONOSUPPORT);
}
Socket::Unix(UnixSocket::new(
file_flags,
socket_type,
&ctx.objs.host.abstract_unix_namespace(),
))
}
libc::AF_INET => match socket_type {
libc::SOCK_STREAM => {
if protocol != 0 && protocol != libc::IPPROTO_TCP {
log::debug!("Unsupported inet stream socket protocol {protocol}");
return Err(Errno::EPROTONOSUPPORT);
}
if ctx.objs.host.params.use_new_tcp {
Socket::Inet(InetSocket::Tcp(TcpSocket::new(file_flags)))
} else {
Socket::Inet(InetSocket::LegacyTcp(LegacyTcpSocket::new(
file_flags,
ctx.objs.host,
)))
}
}
libc::SOCK_DGRAM => {
if protocol != 0 && protocol != libc::IPPROTO_UDP {
log::debug!("Unsupported inet dgram socket protocol {protocol}");
return Err(Errno::EPROTONOSUPPORT);
}
let send_buf_size = ctx.objs.host.params.init_sock_send_buf_size;
let recv_buf_size = ctx.objs.host.params.init_sock_recv_buf_size;
Socket::Inet(InetSocket::Udp(UdpSocket::new(
file_flags,
send_buf_size.try_into().unwrap(),
recv_buf_size.try_into().unwrap(),
)))
}
_ => return Err(Errno::ESOCKTNOSUPPORT),
},
libc::AF_NETLINK => {
let socket_type = match NetlinkSocketType::try_from(socket_type) {
Ok(x) => x,
Err(e) => {
warn!("{}", e);
return Err(Errno::EPROTONOSUPPORT);
}
};
let family = match NetlinkFamily::try_from(protocol) {
Ok(x) => x,
Err(e) => {
warn!("{}", e);
return Err(Errno::EPROTONOSUPPORT);
}
};
Socket::Netlink(NetlinkSocket::new(file_flags, socket_type, family))
}
_ => return Err(Errno::EAFNOSUPPORT),
};
let mut desc = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(socket))));
desc.set_flags(descriptor_flags);
let fd = ctx
.objs
.thread
.descriptor_table_borrow_mut(ctx.objs.host)
.register_descriptor(desc)
.or(Err(Errno::ENFILE))?;
log::trace!("Created socket fd {fd}");
Ok(fd)
}
log_syscall!(
bind,
std::ffi::c_int,
std::ffi::c_int,
SyscallSockAddrArg<2>,
libc::socklen_t,
);
pub fn bind(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len: libc::socklen_t,
) -> Result<(), SyscallError> {
let file = {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.inner_file().clone()
};
let File::Socket(ref socket) = file else {
return Err(Errno::ENOTSOCK.into());
};
let addr = io::read_sockaddr(&ctx.objs.process.memory_borrow(), addr_ptr, addr_len)?;
log::trace!("Attempting to bind fd {} to {:?}", fd, addr);
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
Socket::bind(socket, addr.as_ref(), &net_ns, &mut *rng)
}
log_syscall!(
sendto,
libc::ssize_t,
std::ffi::c_int,
SyscallBufferArg<2>,
libc::size_t,
nix::sys::socket::MsgFlags,
SyscallSockAddrArg<5>,
libc::socklen_t,
);
pub fn sendto(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
buf_ptr: ForeignPtr<u8>,
buf_len: libc::size_t,
flags: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len: libc::socklen_t,
) -> Result<libc::ssize_t, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.clone()
}
};
let File::Socket(ref socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mut mem = ctx.objs.process.memory_borrow_mut();
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
let addr = io::read_sockaddr(&mem, addr_ptr, addr_len)?;
log::trace!("Attempting to send {} bytes to {:?}", buf_len, addr);
let iov = IoVec {
base: buf_ptr,
len: buf_len,
};
let args = SendmsgArgs {
addr,
iovs: &[iov],
control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
flags,
};
let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
});
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
let bytes_sent = result?;
Ok(bytes_sent)
}
log_syscall!(
sendmsg,
libc::ssize_t,
std::ffi::c_int,
*const libc::msghdr,
nix::sys::socket::MsgFlags,
);
pub fn sendmsg(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
msg_ptr: ForeignPtr<libc::msghdr>,
flags: std::ffi::c_int,
) -> Result<libc::ssize_t, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
match Self::get_descriptor(&desc_table, fd)?.file() {
CompatFile::New(file) => file.clone(),
CompatFile::Legacy(_file) => {
return Err(Errno::ENOTSOCK.into());
}
}
}
};
let File::Socket(ref socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mut mem = ctx.objs.process.memory_borrow_mut();
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
let msg = io::read_msghdr(&mem, msg_ptr)?;
let args = SendmsgArgs {
addr: io::read_sockaddr(&mem, msg.name, msg.name_len)?,
iovs: &msg.iovs,
control_ptr: ForeignArrayPtr::new(msg.control, msg.control_len),
flags,
};
let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
});
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
let bytes_written = result?;
Ok(bytes_written)
}
log_syscall!(
recvfrom,
libc::ssize_t,
std::ffi::c_int,
*const std::ffi::c_void,
libc::size_t,
nix::sys::socket::MsgFlags,
*const libc::sockaddr,
*const libc::socklen_t,
);
pub fn recvfrom(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
buf_ptr: ForeignPtr<u8>,
buf_len: libc::size_t,
flags: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
) -> Result<libc::ssize_t, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.clone()
}
};
let File::Socket(ref socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mut mem = ctx.objs.process.memory_borrow_mut();
log::trace!("Attempting to recv {} bytes", buf_len);
let iov = IoVec {
base: buf_ptr,
len: buf_len,
};
let args = RecvmsgArgs {
iovs: &[iov],
control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
flags,
};
let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::recvmsg(socket, args, &mut mem, cb_queue)
});
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
let RecvmsgReturn {
return_val,
addr: from_addr,
..
} = result?;
if !addr_ptr.is_null() {
io::write_sockaddr_and_len(&mut mem, from_addr.as_ref(), addr_ptr, addr_len_ptr)?;
}
Ok(return_val)
}
log_syscall!(
recvmsg,
libc::ssize_t,
std::ffi::c_int,
*const libc::msghdr,
nix::sys::socket::MsgFlags,
);
pub fn recvmsg(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
msg_ptr: ForeignPtr<libc::msghdr>,
flags: std::ffi::c_int,
) -> Result<libc::ssize_t, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
match Self::get_descriptor(&desc_table, fd)?.file() {
CompatFile::New(file) => file.clone(),
CompatFile::Legacy(_file) => {
return Err(Errno::ENOTSOCK.into());
}
}
}
};
let File::Socket(ref socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mut mem = ctx.objs.process.memory_borrow_mut();
let mut msg = io::read_msghdr(&mem, msg_ptr)?;
let args = RecvmsgArgs {
iovs: &msg.iovs,
control_ptr: ForeignArrayPtr::new(msg.control, msg.control_len),
flags,
};
let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::recvmsg(socket, args, &mut mem, cb_queue)
});
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
let result = result?;
if !msg.name.is_null() {
if let Some(from_addr) = result.addr.as_ref() {
msg.name_len = io::write_sockaddr(&mut mem, from_addr, msg.name, msg.name_len)?;
} else {
msg.name_len = 0;
}
}
msg.control_len = result.control_len;
msg.flags = result.msg_flags;
io::update_msghdr(&mut mem, msg_ptr, msg)?;
Ok(result.return_val)
}
log_syscall!(
getsockname,
std::ffi::c_int,
std::ffi::c_int,
*const libc::sockaddr,
*const libc::socklen_t,
);
pub fn getsockname(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
) -> Result<(), Errno> {
let addr_to_write: Option<SockaddrStorage> = {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK);
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK);
};
if addr_ptr.is_null() || addr_len_ptr.is_null() {
return Err(Errno::EFAULT);
}
let socket = socket.borrow();
socket.getsockname()?
};
debug!("Returning socket address of {:?}", addr_to_write);
io::write_sockaddr_and_len(
&mut ctx.objs.process.memory_borrow_mut(),
addr_to_write.as_ref(),
addr_ptr,
addr_len_ptr,
)?;
Ok(())
}
log_syscall!(
getpeername,
std::ffi::c_int,
std::ffi::c_int,
*const libc::sockaddr,
*const libc::socklen_t,
);
pub fn getpeername(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
) -> Result<(), Errno> {
let addr_to_write = {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK);
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK);
};
if addr_ptr.is_null() || addr_len_ptr.is_null() {
return Err(Errno::EFAULT);
}
#[allow(clippy::let_and_return)]
let addr_to_write = socket.borrow().getpeername()?;
addr_to_write
};
debug!("Returning peer address of {:?}", addr_to_write);
io::write_sockaddr_and_len(
&mut ctx.objs.process.memory_borrow_mut(),
addr_to_write.as_ref(),
addr_ptr,
addr_len_ptr,
)?;
Ok(())
}
log_syscall!(
listen,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_int,
);
pub fn listen(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
backlog: std::ffi::c_int,
) -> Result<(), Errno> {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK);
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK);
};
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::listen(socket, backlog, &net_ns, &mut *rng, cb_queue)
})?;
Ok(())
}
log_syscall!(
accept,
std::ffi::c_int,
std::ffi::c_int,
*const libc::sockaddr,
*const libc::socklen_t,
);
pub fn accept(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
) -> Result<DescriptorHandle, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.clone()
}
};
let mut result = Self::accept_helper(ctx, file.inner_file(), addr_ptr, addr_len_ptr, 0);
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
result
}
log_syscall!(
accept4,
std::ffi::c_int,
std::ffi::c_int,
*const libc::sockaddr,
*const libc::socklen_t,
std::ffi::c_int,
);
pub fn accept4(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
flags: std::ffi::c_int,
) -> Result<DescriptorHandle, SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.clone()
}
};
let mut result = Self::accept_helper(ctx, file.inner_file(), addr_ptr, addr_len_ptr, flags);
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
result
}
fn accept_helper(
ctx: &mut SyscallContext,
file: &File,
addr_ptr: ForeignPtr<u8>,
addr_len_ptr: ForeignPtr<libc::socklen_t>,
flags: std::ffi::c_int,
) -> Result<DescriptorHandle, SyscallError> {
let File::Socket(ref socket) = file else {
return Err(Errno::ENOTSOCK.into());
};
let flags = match SockFlag::from_bits(flags) {
Some(x) => x,
None => {
warn!("Invalid recvfrom flags: {}", flags);
SockFlag::from_bits_truncate(flags)
}
};
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
let result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
socket.borrow_mut().accept(&net_ns, &mut *rng, cb_queue)
});
let file_status = socket.borrow().status();
if result.as_ref().err() == Some(&Errno::EWOULDBLOCK.into())
&& !file_status.contains(FileStatus::NONBLOCK)
{
return Err(SyscallError::new_blocked_on_file(
file.clone(),
FileState::READABLE,
socket.borrow().supports_sa_restart(),
));
}
let new_socket = result?;
let from_addr = {
let File::Socket(new_socket) = new_socket.inner_file() else {
panic!("Accepted file should be a socket");
};
new_socket.borrow().getpeername().unwrap()
};
if !addr_ptr.is_null() {
io::write_sockaddr_and_len(
&mut ctx.objs.process.memory_borrow_mut(),
from_addr.as_ref(),
addr_ptr,
addr_len_ptr,
)?;
}
if flags.contains(SockFlag::SOCK_NONBLOCK) {
new_socket
.inner_file()
.borrow_mut()
.set_status(FileStatus::NONBLOCK);
}
let mut new_desc = Descriptor::new(CompatFile::New(new_socket));
if flags.contains(SockFlag::SOCK_CLOEXEC) {
new_desc.set_flags(DescriptorFlags::FD_CLOEXEC);
}
Ok(ctx
.objs
.thread
.descriptor_table_borrow_mut(ctx.objs.host)
.register_descriptor(new_desc)
.or(Err(Errno::ENFILE))?)
}
log_syscall!(
connect,
std::ffi::c_int,
std::ffi::c_int,
SyscallSockAddrArg<2>,
libc::socklen_t,
);
pub fn connect(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
addr_ptr: ForeignPtr<u8>,
addr_len: libc::socklen_t,
) -> Result<(), SyscallError> {
let file = ctx
.objs
.thread
.syscall_condition()
.and_then(|x| x.active_file().cloned());
let file = match file {
Some(x) => x,
None => {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
return Err(Errno::ENOTSOCK.into());
};
file.clone()
}
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let addr = io::read_sockaddr(&ctx.objs.process.memory_borrow(), addr_ptr, addr_len)?
.ok_or(Errno::EFAULT)?;
let mut rng = ctx.objs.host.random_mut();
let net_ns = ctx.objs.host.network_namespace_borrow();
let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
Socket::connect(socket, &addr, &net_ns, &mut *rng, cb_queue)
});
if let Some(err) = result.as_mut().err() {
if let Some(cond) = err.blocked_condition() {
cond.set_active_file(file);
}
}
result?;
Ok(())
}
log_syscall!(
shutdown,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_uint,
);
pub fn shutdown(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
how: std::ffi::c_uint,
) -> Result<(), SyscallError> {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK.into());
};
let how = Shutdown::try_from(how).or(Err(Errno::EINVAL))?;
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
socket.borrow_mut().shutdown(how, cb_queue)
})?;
Ok(())
}
log_syscall!(
socketpair,
std::ffi::c_int,
linux_api::socket::AddressFamily,
std::ffi::c_int,
std::ffi::c_int,
[std::ffi::c_int; 2],
);
pub fn socketpair(
ctx: &mut SyscallContext,
domain: std::ffi::c_int,
socket_type: std::ffi::c_int,
protocol: std::ffi::c_int,
fd_ptr: ForeignPtr<[std::ffi::c_int; 2]>,
) -> Result<(), SyscallError> {
let flags = socket_type & (libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC);
let socket_type = socket_type & !flags;
if domain != libc::AF_UNIX {
warn!("Domain {domain} is not supported for socketpair()");
return Err(Errno::EOPNOTSUPP.into());
}
let socket_type = match UnixSocketType::try_from(socket_type) {
Ok(x) => x,
Err(e) => {
warn!("Not a unix socket type: {e}");
return Err(Errno::EPROTONOSUPPORT.into());
}
};
if protocol != 0 {
warn!("Unsupported socket protocol {protocol}, we only support default protocol 0");
return Err(Errno::EPROTONOSUPPORT.into());
}
let mut file_flags = FileStatus::empty();
let mut descriptor_flags = DescriptorFlags::empty();
if flags & libc::SOCK_NONBLOCK != 0 {
file_flags.insert(FileStatus::NONBLOCK);
}
if flags & libc::SOCK_CLOEXEC != 0 {
descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC);
}
let (socket_1, socket_2) = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
UnixSocket::pair(
file_flags,
socket_type,
&ctx.objs.host.abstract_unix_namespace(),
cb_queue,
)
});
let mut desc_1 = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
Socket::Unix(socket_1),
))));
let mut desc_2 = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
Socket::Unix(socket_2),
))));
desc_1.set_flags(descriptor_flags);
desc_2.set_flags(descriptor_flags);
let mut dt = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
let fd_1 = dt.register_descriptor(desc_1).unwrap();
let fd_2 = dt.register_descriptor(desc_2).unwrap();
let fds = [i32::from(fd_1), i32::from(fd_2)];
let write_res = ctx.objs.process.memory_borrow_mut().write(fd_ptr, &fds);
match write_res {
Ok(_) => Ok(()),
Err(e) => {
CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
dt.deregister_descriptor(fd_1)
.unwrap()
.close(ctx.objs.host, cb_queue);
dt.deregister_descriptor(fd_2)
.unwrap()
.close(ctx.objs.host, cb_queue);
});
Err(e.into())
}
}
}
log_syscall!(
getsockopt,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_int,
*const std::ffi::c_void,
*const libc::socklen_t,
);
pub fn getsockopt(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
level: std::ffi::c_int,
optname: std::ffi::c_int,
optval_ptr: ForeignPtr<()>,
optlen_ptr: ForeignPtr<libc::socklen_t>,
) -> Result<(), SyscallError> {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK.into());
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mut mem = ctx.objs.process.memory_borrow_mut();
let optlen = mem.read(optlen_ptr)?;
let mut optlen_new = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
socket
.borrow_mut()
.getsockopt(level, optname, optval_ptr, optlen, &mut mem, cb_queue)
})?;
if optlen_new > optlen {
log::warn!(
"Attempting to return an optlen {} that's greater than the provided optlen {}",
optlen_new,
optlen
);
optlen_new = optlen;
}
mem.write(optlen_ptr, &optlen_new)?;
Ok(())
}
log_syscall!(
setsockopt,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_int,
std::ffi::c_int,
*const std::ffi::c_void,
libc::socklen_t,
);
pub fn setsockopt(
ctx: &mut SyscallContext,
fd: std::ffi::c_int,
level: std::ffi::c_int,
optname: std::ffi::c_int,
optval_ptr: ForeignPtr<()>,
optlen: libc::socklen_t,
) -> Result<(), SyscallError> {
let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
let desc = Self::get_descriptor(&desc_table, fd)?;
let CompatFile::New(file) = desc.file() else {
return Err(Errno::ENOTSOCK.into());
};
let File::Socket(socket) = file.inner_file() else {
return Err(Errno::ENOTSOCK.into());
};
let mem = ctx.objs.process.memory_borrow();
socket
.borrow_mut()
.setsockopt(level, optname, optval_ptr, optlen, &mem)?;
Ok(())
}
}