//! linux_raw syscalls supporting `rustix::net`. //! //! # Safety //! //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use super::msghdr::{ with_noaddr_msghdr, with_recv_msghdr, with_unix_msghdr, with_v4_msghdr, with_v6_msghdr, }; use super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}; use super::send_recv::{RecvFlags, SendFlags}; use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; use crate::backend::c; use crate::backend::conv::{ by_mut, by_ref, c_int, c_uint, ret, ret_owned_fd, ret_usize, size_of, slice, slice_mut, socklen_t, zero, }; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{ AddressFamily, Protocol, RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer, Shutdown, SocketAddrAny, SocketAddrUnix, SocketAddrV4, SocketAddrV6, SocketFlags, SocketType, }; use c::{sockaddr, sockaddr_in, sockaddr_in6, socklen_t}; use core::mem::MaybeUninit; #[cfg(target_arch = "x86")] use { crate::backend::conv::{slice_just_addr, x86_sys}, crate::backend::reg::{ArgReg, SocketArg}, linux_raw_sys::net::{ SYS_ACCEPT, SYS_ACCEPT4, SYS_BIND, SYS_CONNECT, SYS_GETPEERNAME, SYS_GETSOCKNAME, SYS_LISTEN, SYS_RECV, SYS_RECVFROM, SYS_RECVMSG, SYS_SEND, SYS_SENDMSG, SYS_SENDTO, SYS_SHUTDOWN, SYS_SOCKET, SYS_SOCKETPAIR, }, }; #[inline] pub(crate) fn socket( family: AddressFamily, type_: SocketType, protocol: Option, ) -> io::Result { #[cfg(not(target_arch = "x86"))] unsafe { ret_owned_fd(syscall_readonly!(__NR_socket, family, type_, protocol)) } #[cfg(target_arch = "x86")] unsafe { ret_owned_fd(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SOCKET), slice_just_addr::, _>(&[ family.into(), type_.into(), protocol.into(), ]) )) } } #[inline] pub(crate) fn socket_with( family: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Option, ) -> io::Result { #[cfg(not(target_arch = "x86"))] unsafe { ret_owned_fd(syscall_readonly!( __NR_socket, family, (type_, flags), protocol )) } #[cfg(target_arch = "x86")] unsafe { ret_owned_fd(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SOCKET), slice_just_addr::, _>(&[ family.into(), (type_, flags).into(), protocol.into(), ]) )) } } #[inline] pub(crate) fn socketpair( family: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Option, ) -> io::Result<(OwnedFd, OwnedFd)> { #[cfg(not(target_arch = "x86"))] unsafe { let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); ret(syscall!( __NR_socketpair, family, (type_, flags), protocol, &mut result ))?; let [fd0, fd1] = result.assume_init(); Ok((fd0, fd1)) } #[cfg(target_arch = "x86")] unsafe { let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); ret(syscall!( __NR_socketcall, x86_sys(SYS_SOCKETPAIR), slice_just_addr::, _>(&[ family.into(), (type_, flags).into(), protocol.into(), (&mut result).into(), ]) ))?; let [fd0, fd1] = result.assume_init(); Ok((fd0, fd1)) } } #[inline] pub(crate) fn accept(fd: BorrowedFd<'_>) -> io::Result { #[cfg(not(target_arch = "x86"))] unsafe { let fd = ret_owned_fd(syscall_readonly!(__NR_accept, fd, zero(), zero()))?; Ok(fd) } #[cfg(target_arch = "x86")] unsafe { let fd = ret_owned_fd(syscall_readonly!( __NR_socketcall, x86_sys(SYS_ACCEPT), slice_just_addr::, _>(&[fd.into(), zero(), zero()]) ))?; Ok(fd) } } #[inline] pub(crate) fn accept_with(fd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result { #[cfg(not(target_arch = "x86"))] unsafe { let fd = ret_owned_fd(syscall_readonly!(__NR_accept4, fd, zero(), zero(), flags))?; Ok(fd) } #[cfg(target_arch = "x86")] unsafe { let fd = ret_owned_fd(syscall_readonly!( __NR_socketcall, x86_sys(SYS_ACCEPT4), slice_just_addr::, _>(&[fd.into(), zero(), zero(), flags.into()]) ))?; Ok(fd) } } #[inline] pub(crate) fn acceptfrom(fd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option)> { #[cfg(not(target_arch = "x86"))] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); let fd = ret_owned_fd(syscall!( __NR_accept, fd, &mut storage, by_mut(&mut addrlen) ))?; Ok(( fd, maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), )) } #[cfg(target_arch = "x86")] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); let fd = ret_owned_fd(syscall!( __NR_socketcall, x86_sys(SYS_ACCEPT), slice_just_addr::, _>(&[ fd.into(), (&mut storage).into(), by_mut(&mut addrlen), ]) ))?; Ok(( fd, maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), )) } } #[inline] pub(crate) fn acceptfrom_with( fd: BorrowedFd<'_>, flags: SocketFlags, ) -> io::Result<(OwnedFd, Option)> { #[cfg(not(target_arch = "x86"))] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); let fd = ret_owned_fd(syscall!( __NR_accept4, fd, &mut storage, by_mut(&mut addrlen), flags ))?; Ok(( fd, maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), )) } #[cfg(target_arch = "x86")] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); let fd = ret_owned_fd(syscall!( __NR_socketcall, x86_sys(SYS_ACCEPT4), slice_just_addr::, _>(&[ fd.into(), (&mut storage).into(), by_mut(&mut addrlen), flags.into(), ]) ))?; Ok(( fd, maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), )) } } #[inline] pub(crate) fn recvmsg( sockfd: BorrowedFd<'_>, iov: &mut [IoSliceMut<'_>], control: &mut RecvAncillaryBuffer<'_>, msg_flags: RecvFlags, ) -> io::Result { let mut storage = MaybeUninit::::uninit(); with_recv_msghdr(&mut storage, iov, control, |msghdr| { #[cfg(not(target_arch = "x86"))] let result = unsafe { ret_usize(syscall!(__NR_recvmsg, sockfd, by_mut(msghdr), msg_flags)) }; #[cfg(target_arch = "x86")] let result = unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_RECVMSG), slice_just_addr::, _>(&[ sockfd.into(), by_mut(msghdr), msg_flags.into(), ]) )) }; result.map(|bytes| { // Get the address of the sender, if any. let addr = unsafe { maybe_read_sockaddr_os(msghdr.msg_name as _, msghdr.msg_namelen as _) }; RecvMsgReturn { bytes, address: addr, flags: RecvFlags::from_bits_retain(msghdr.msg_flags), } }) }) } #[inline] pub(crate) fn sendmsg( sockfd: BorrowedFd<'_>, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, msg_flags: SendFlags, ) -> io::Result { with_noaddr_msghdr(iov, control, |msghdr| { #[cfg(not(target_arch = "x86"))] let result = unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; #[cfg(target_arch = "x86")] let result = unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_SENDMSG), slice_just_addr::, _>(&[ sockfd.into(), by_ref(&msghdr), msg_flags.into() ]) )) }; result }) } #[inline] pub(crate) fn sendmsg_v4( sockfd: BorrowedFd<'_>, addr: &SocketAddrV4, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, msg_flags: SendFlags, ) -> io::Result { with_v4_msghdr(addr, iov, control, |msghdr| { #[cfg(not(target_arch = "x86"))] let result = unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; #[cfg(target_arch = "x86")] let result = unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_SENDMSG), slice_just_addr::, _>(&[ sockfd.into(), by_ref(&msghdr), msg_flags.into(), ]) )) }; result }) } #[inline] pub(crate) fn sendmsg_v6( sockfd: BorrowedFd<'_>, addr: &SocketAddrV6, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, msg_flags: SendFlags, ) -> io::Result { with_v6_msghdr(addr, iov, control, |msghdr| { #[cfg(not(target_arch = "x86"))] let result = unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; #[cfg(target_arch = "x86")] let result = unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_SENDMSG), slice_just_addr::, _>(&[ sockfd.into(), by_ref(&msghdr), msg_flags.into() ]) )) }; result }) } #[inline] pub(crate) fn sendmsg_unix( sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, msg_flags: SendFlags, ) -> io::Result { with_unix_msghdr(addr, iov, control, |msghdr| { #[cfg(not(target_arch = "x86"))] let result = unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; #[cfg(target_arch = "x86")] let result = unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_SENDMSG), slice_just_addr::, _>(&[ sockfd.into(), by_ref(&msghdr), msg_flags.into() ]) )) }; result }) } #[inline] pub(crate) fn shutdown(fd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_shutdown, fd, c_uint(how as c::c_uint) )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SHUTDOWN), slice_just_addr::, _>(&[fd.into(), c_uint(how as c::c_uint)]) )) } } #[inline] pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result { let (buf_addr, buf_len) = slice(buf); #[cfg(not(any( target_arch = "aarch64", target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", target_arch = "x86", target_arch = "x86_64", )))] unsafe { ret_usize(syscall_readonly!(__NR_send, fd, buf_addr, buf_len, flags)) } #[cfg(any( target_arch = "aarch64", target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", target_arch = "x86_64", ))] unsafe { ret_usize(syscall_readonly!( __NR_sendto, fd, buf_addr, buf_len, flags, zero(), zero() )) } #[cfg(target_arch = "x86")] unsafe { ret_usize(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SEND), slice_just_addr::, _>(&[ fd.into(), buf_addr, buf_len, flags.into() ]) )) } } #[inline] pub(crate) fn sendto_v4( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV4, ) -> io::Result { let (buf_addr, buf_len) = slice(buf); #[cfg(not(target_arch = "x86"))] unsafe { ret_usize(syscall_readonly!( __NR_sendto, fd, buf_addr, buf_len, flags, by_ref(&encode_sockaddr_v4(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret_usize(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SENDTO), slice_just_addr::, _>(&[ fd.into(), buf_addr, buf_len, flags.into(), by_ref(&encode_sockaddr_v4(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn sendto_v6( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV6, ) -> io::Result { let (buf_addr, buf_len) = slice(buf); #[cfg(not(target_arch = "x86"))] unsafe { ret_usize(syscall_readonly!( __NR_sendto, fd, buf_addr, buf_len, flags, by_ref(&encode_sockaddr_v6(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret_usize(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SENDTO), slice_just_addr::, _>(&[ fd.into(), buf_addr, buf_len, flags.into(), by_ref(&encode_sockaddr_v6(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn sendto_unix( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrUnix, ) -> io::Result { let (buf_addr, buf_len) = slice(buf); #[cfg(not(target_arch = "x86"))] unsafe { ret_usize(syscall_readonly!( __NR_sendto, fd, buf_addr, buf_len, flags, by_ref(&addr.unix), socklen_t(addr.addr_len()) )) } #[cfg(target_arch = "x86")] unsafe { ret_usize(syscall_readonly!( __NR_socketcall, x86_sys(SYS_SENDTO), slice_just_addr::, _>(&[ fd.into(), buf_addr, buf_len, flags.into(), by_ref(&addr.unix), socklen_t(addr.addr_len()), ]) )) } } #[inline] pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result { let (buf_addr_mut, buf_len) = slice_mut(buf); #[cfg(not(any( target_arch = "aarch64", target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", target_arch = "x86", target_arch = "x86_64", )))] unsafe { ret_usize(syscall!(__NR_recv, fd, buf_addr_mut, buf_len, flags)) } #[cfg(any( target_arch = "aarch64", target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", target_arch = "x86_64", ))] unsafe { ret_usize(syscall!( __NR_recvfrom, fd, buf_addr_mut, buf_len, flags, zero(), zero() )) } #[cfg(target_arch = "x86")] unsafe { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_RECV), slice_just_addr::, _>(&[ fd.into(), buf_addr_mut, buf_len, flags.into(), ]) )) } } #[inline] pub(crate) fn recvfrom( fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags, ) -> io::Result<(usize, Option)> { let (buf_addr_mut, buf_len) = slice_mut(buf); let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); unsafe { // `recvfrom` does not write to the storage if the socket is // connection-oriented sockets, so we initialize the family field to // `AF_UNSPEC` so that we can detect this case. initialize_family_to_unspec(storage.as_mut_ptr()); #[cfg(not(target_arch = "x86"))] let nread = ret_usize(syscall!( __NR_recvfrom, fd, buf_addr_mut, buf_len, flags, &mut storage, by_mut(&mut addrlen) ))?; #[cfg(target_arch = "x86")] let nread = ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_RECVFROM), slice_just_addr::, _>(&[ fd.into(), buf_addr_mut, buf_len, flags.into(), (&mut storage).into(), by_mut(&mut addrlen), ]) ))?; Ok(( nread, maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), )) } } #[inline] pub(crate) fn getpeername(fd: BorrowedFd<'_>) -> io::Result> { #[cfg(not(target_arch = "x86"))] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); ret(syscall!( __NR_getpeername, fd, &mut storage, by_mut(&mut addrlen) ))?; Ok(maybe_read_sockaddr_os( &storage.assume_init(), addrlen.try_into().unwrap(), )) } #[cfg(target_arch = "x86")] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); ret(syscall!( __NR_socketcall, x86_sys(SYS_GETPEERNAME), slice_just_addr::, _>(&[ fd.into(), (&mut storage).into(), by_mut(&mut addrlen), ]) ))?; Ok(maybe_read_sockaddr_os( &storage.assume_init(), addrlen.try_into().unwrap(), )) } } #[inline] pub(crate) fn getsockname(fd: BorrowedFd<'_>) -> io::Result { #[cfg(not(target_arch = "x86"))] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); ret(syscall!( __NR_getsockname, fd, &mut storage, by_mut(&mut addrlen) ))?; Ok(read_sockaddr_os( &storage.assume_init(), addrlen.try_into().unwrap(), )) } #[cfg(target_arch = "x86")] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); ret(syscall!( __NR_socketcall, x86_sys(SYS_GETSOCKNAME), slice_just_addr::, _>(&[ fd.into(), (&mut storage).into(), by_mut(&mut addrlen), ]) ))?; Ok(read_sockaddr_os( &storage.assume_init(), addrlen.try_into().unwrap(), )) } } #[inline] pub(crate) fn bind_v4(fd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_bind, fd, by_ref(&encode_sockaddr_v4(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_BIND), slice_just_addr::, _>(&[ fd.into(), by_ref(&encode_sockaddr_v4(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn bind_v6(fd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_bind, fd, by_ref(&encode_sockaddr_v6(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_BIND), slice_just_addr::, _>(&[ fd.into(), by_ref(&encode_sockaddr_v6(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn bind_unix(fd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_bind, fd, by_ref(&addr.unix), socklen_t(addr.addr_len()) )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_BIND), slice_just_addr::, _>(&[ fd.into(), by_ref(&addr.unix), socklen_t(addr.addr_len()), ]) )) } } #[inline] pub(crate) fn connect_v4(fd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_connect, fd, by_ref(&encode_sockaddr_v4(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_CONNECT), slice_just_addr::, _>(&[ fd.into(), by_ref(&encode_sockaddr_v4(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn connect_v6(fd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_connect, fd, by_ref(&encode_sockaddr_v6(addr)), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_CONNECT), slice_just_addr::, _>(&[ fd.into(), by_ref(&encode_sockaddr_v6(addr)), size_of::(), ]) )) } } #[inline] pub(crate) fn connect_unix(fd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_connect, fd, by_ref(&addr.unix), socklen_t(addr.addr_len()) )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_CONNECT), slice_just_addr::, _>(&[ fd.into(), by_ref(&addr.unix), socklen_t(addr.addr_len()), ]) )) } } #[inline] pub(crate) fn connect_unspec(fd: BorrowedFd<'_>) -> io::Result<()> { debug_assert_eq!(c::AF_UNSPEC, 0); let addr = MaybeUninit::::zeroed(); #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( __NR_connect, fd, by_ref(&addr), size_of::() )) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_CONNECT), slice_just_addr::, _>(&[ fd.into(), by_ref(&addr), size_of::(), ]) )) } } #[inline] pub(crate) fn listen(fd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!(__NR_listen, fd, c_int(backlog))) } #[cfg(target_arch = "x86")] unsafe { ret(syscall_readonly!( __NR_socketcall, x86_sys(SYS_LISTEN), slice_just_addr::, _>(&[fd.into(), c_int(backlog)]) )) } }