Refactor host socket

1. Add Rust memory-safe types, e.g., socket_address, address_family and socket_type;
2. Implement Berkeley Sockets API for HostSocket.
This commit is contained in:
He Sun 2020-09-24 07:38:28 +08:00 committed by Tate, Hongliang Tian
parent 230e6fa380
commit d590486029
20 changed files with 722 additions and 393 deletions

@ -93,8 +93,8 @@ impl EpollFile {
pub fn control(&self, op: EpollCtlCmd, fd: FileDesc, event: Option<&EpollEvent>) -> Result<()> { pub fn control(&self, op: EpollCtlCmd, fd: FileDesc, event: Option<&EpollEvent>) -> Result<()> {
let host_fd = { let host_fd = {
let fd_ref = current!().file(fd)?; let fd_ref = current!().file(fd)?;
if let Ok(socket) = fd_ref.as_socket() { if let Ok(socket) = fd_ref.as_host_socket() {
socket.fd() socket.host_fd()
} else if let Ok(eventfd) = fd_ref.as_event() { } else if let Ok(eventfd) = fd_ref.as_event() {
eventfd.get_host_fd() eventfd.get_host_fd()
} else if let Ok(epoll_file) = fd_ref.as_epfile() { } else if let Ok(epoll_file) = fd_ref.as_epfile() {

@ -112,8 +112,8 @@ pub fn do_poll(pollfds: &mut [PollEvent], timeout: *mut timeval_t) -> Result<usi
continue; continue;
} }
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let fd = socket.fd() as FileDesc; let fd = socket.host_fd() as FileDesc;
index_host_pollfds.push(i); index_host_pollfds.push(i);
host_pollfds.push(PollEvent::new(fd, pollfd.events())); host_pollfds.push(PollEvent::new(fd, pollfd.events()));
} else if let Ok(eventfd) = file_ref.as_event() { } else if let Ok(eventfd) = file_ref.as_event() {

@ -2,21 +2,17 @@ use super::*;
use std; use std;
use untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc}; use untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc};
mod io_multiplexing;
mod iovs;
mod msg;
mod msg_flags;
mod socket_file;
mod syscalls;
mod unix_socket;
pub use self::io_multiplexing::{ pub use self::io_multiplexing::{
clear_notifier_status, notify_thread, wait_for_notification, EpollEvent, IoEvent, PollEvent, clear_notifier_status, notify_thread, wait_for_notification, EpollEvent, IoEvent, PollEvent,
PollEventFlags, THREAD_NOTIFIERS, PollEventFlags, THREAD_NOTIFIERS,
}; };
pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec}; pub use self::socket::{
pub use self::msg::{msghdr, msghdr_mut, MsgHdr, MsgHdrMut}; msghdr, msghdr_mut, AddressFamily, AsUnixSocket, FileFlags, HostSocket, HostSocketType, Iovs,
pub use self::msg_flags::{MsgHdrFlags, RecvFlags, SendFlags}; IovsMut, MsgHdr, MsgHdrFlags, MsgHdrMut, RecvFlags, SendFlags, SliceAsLibcIovec, SockAddr,
pub use self::socket_file::{AsSocket, SocketFile}; SocketType, UnixSocketFile,
};
pub use self::syscalls::*; pub use self::syscalls::*;
pub use self::unix_socket::{AsUnixSocket, UnixSocketFile};
mod io_multiplexing;
mod socket;
mod syscalls;

@ -0,0 +1,71 @@
use super::*;
// The protocol family generally is the same as the address family
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum AddressFamily {
UNSPEC = 0,
LOCAL = 1,
/* Hide the families with the same number
UNIX = LOCAL,
FILE = LOCAL,
*/
INET = 2,
AX25 = 3,
IPX = 4,
APPLETALK = 5,
NETROM = 6,
BRIDGE = 7,
ATMPVC = 8,
X25 = 9,
INET6 = 10,
ROSE = 11,
DECnet = 12,
NETBEUI = 13,
SECURITY = 14,
KEY = 15,
NETLINK = 16,
/* Hide the family with the same number
ROUTE = NETLINK,
*/
PACKET = 17,
ASH = 18,
ECONET = 19,
ATMSVC = 20,
RDS = 21,
SNA = 22,
IRDA = 23,
PPPOX = 24,
WANPIPE = 25,
LLC = 26,
IB = 27,
MPLS = 28,
CAN = 29,
TIPC = 30,
BLUETOOTH = 31,
IUCV = 32,
RXRPC = 33,
ISDN = 34,
PHONET = 35,
IEEE802154 = 36,
CAIF = 37,
ALG = 38,
NFC = 39,
VSOCK = 40,
KCM = 41,
QIPCRTR = 42,
SMC = 43,
XDP = 44,
MAX = 45,
}
impl AddressFamily {
pub fn try_from(af: u16) -> Result<Self> {
if af >= Self::MAX as u16 {
return_errno!(EINVAL, "Unknown address family");
} else {
Ok(unsafe { core::mem::transmute(af) })
}
}
}

@ -34,3 +34,10 @@ bitflags! {
const MSG_NOTIFICATION = 0x8000; // Only applicable to SCTP socket const MSG_NOTIFICATION = 0x8000; // Only applicable to SCTP socket
} }
} }
bitflags! {
pub struct FileFlags: i32 {
const SOCK_NONBLOCK = 0x800;
const SOCK_CLOEXEC = 0x80000;
}
}

@ -0,0 +1,132 @@
use super::*;
use crate::fs::{
occlum_ocall_ioctl, AccessMode, CreationFlags, File, FileRef, IoctlCmd, StatusFlags,
};
use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write};
use std::mem;
/// Native linux socket
#[derive(Debug)]
pub struct HostSocket {
host_fd: c_int,
}
impl HostSocket {
pub fn new(
domain: AddressFamily,
socket_type: SocketType,
file_flags: FileFlags,
protocol: i32,
) -> Result<Self> {
let host_fd = try_libc!(libc::ocall::socket(
domain as i32,
socket_type as i32 | file_flags.bits(),
protocol
));
Ok(Self { host_fd })
}
pub fn host_fd(&self) -> c_int {
self.host_fd
}
pub fn bind(&self, addr: &SockAddr) -> Result<()> {
let (addr_ptr, addr_len) = addr.as_ptr_and_len();
let ret = try_libc!(libc::ocall::bind(
self.host_fd(),
addr_ptr as *const libc::sockaddr,
addr_len as u32
));
Ok(())
}
pub fn listen(&self, backlog: i32) -> Result<()> {
let ret = try_libc!(libc::ocall::listen(self.host_fd(), backlog));
Ok(())
}
pub fn accept(&self, flags: FileFlags) -> Result<(Self, Option<SockAddr>)> {
let mut sockaddr = SockAddr::default();
let mut addr_len = sockaddr.len();
let ret = try_libc!(libc::ocall::accept4(
self.host_fd(),
sockaddr.as_mut_ptr() as *mut _,
&mut addr_len as *mut _ as *mut _,
flags.bits()
));
let addr_option = if addr_len != 0 {
sockaddr.set_len(addr_len)?;
Some(sockaddr)
} else {
None
};
Ok((Self { host_fd: ret }, addr_option))
}
pub fn connect(&self, addr: &Option<SockAddr>) -> Result<()> {
debug!("host_fd: {} addr {:?}", self.host_fd(), addr);
let (addr_ptr, addr_len) = if let Some(sock_addr) = addr {
sock_addr.as_ptr_and_len()
} else {
(std::ptr::null(), 0)
};
let ret = try_libc!(libc::ocall::connect(
self.host_fd(),
addr_ptr,
addr_len as u32
));
Ok(())
}
pub fn sendto(
&self,
buf: &[u8],
flags: SendFlags,
addr_option: &Option<SockAddr>,
) -> Result<usize> {
let bufs = vec![buf];
let name_option = addr_option.as_ref().map(|addr| addr.as_slice());
self.do_sendmsg(&bufs, flags, name_option, None)
}
pub fn recvfrom(&self, buf: &mut [u8], flags: RecvFlags) -> Result<(usize, Option<SockAddr>)> {
let mut sockaddr = SockAddr::default();
let mut bufs = vec![buf];
let (bytes_recv, addr_len, _, _) =
self.do_recvmsg(&mut bufs, flags, Some(sockaddr.as_mut_slice()), None)?;
let addr_option = if addr_len != 0 {
sockaddr.set_len(addr_len)?;
Some(sockaddr)
} else {
None
};
Ok((bytes_recv, addr_option))
}
}
impl Drop for HostSocket {
fn drop(&mut self) {
let ret = unsafe { libc::ocall::close(self.host_fd) };
assert!(ret == 0);
}
}
pub trait HostSocketType {
fn as_host_socket(&self) -> Result<&HostSocket>;
}
impl HostSocketType for FileRef {
fn as_host_socket(&self) -> Result<&HostSocket> {
self.as_any()
.downcast_ref::<HostSocket>()
.ok_or_else(|| errno!(EBADF, "not a host socket"))
}
}

@ -1,7 +1,7 @@
use super::*; use super::*;
use fs::{occlum_ocall_ioctl, BuiltinIoctlNum, IoctlCmd}; use fs::{occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd};
impl SocketFile { impl HostSocket {
pub(super) fn ioctl_impl(&self, cmd: &mut IoctlCmd) -> Result<i32> { pub(super) fn ioctl_impl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
if let IoctlCmd::SIOCGIFCONF(arg_ref) = cmd { if let IoctlCmd::SIOCGIFCONF(arg_ref) = cmd {
return self.ioctl_getifconf(arg_ref); return self.ioctl_getifconf(arg_ref);
@ -13,7 +13,7 @@ impl SocketFile {
let mut retval: i32 = 0; let mut retval: i32 = 0;
let status = occlum_ocall_ioctl( let status = occlum_ocall_ioctl(
&mut retval as *mut i32, &mut retval as *mut i32,
self.fd(), self.host_fd(),
cmd_num, cmd_num,
cmd_arg_ptr, cmd_arg_ptr,
cmd.arg_len(), cmd.arg_len(),
@ -36,7 +36,7 @@ impl SocketFile {
let mut retval: i32 = 0; let mut retval: i32 = 0;
let status = occlum_ocall_ioctl_repack( let status = occlum_ocall_ioctl_repack(
&mut retval as *mut i32, &mut retval as *mut i32,
self.fd(), self.host_fd(),
BuiltinIoctlNum::SIOCGIFCONF as i32, BuiltinIoctlNum::SIOCGIFCONF as i32,
arg_ref.ifc_buf, arg_ref.ifc_buf,
arg_ref.ifc_len, arg_ref.ifc_len,

@ -0,0 +1,9 @@
use super::*;
mod host_socket;
mod ioctl_impl;
mod recv;
mod send;
mod socket_file;
pub use self::host_socket::{HostSocket, HostSocketType};

@ -1,45 +1,19 @@
use super::*; use super::*;
use crate::untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc}; use crate::untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc};
impl SocketFile { impl HostSocket {
// TODO: need sockaddr type to implement send/sento pub fn recv(&self, buf: &mut [u8], flags: RecvFlags) -> Result<usize> {
/* let (bytes_recvd, _) = self.recvfrom(buf, flags)?;
pub fn recv(&self, buf: &mut [u8], flags: MsgHdrFlags) -> Result<usize> {
let (bytes_recvd, _) = self.recvfrom(buf, flags, None)?;
Ok(bytes_recvd) Ok(bytes_recvd)
} }
pub fn recvfrom(&self, buf: &mut [u8], flags: MsgHdrFlags, src_addr: Option<&mut [u8]>) -> Result<(usize, usize)> {
let (bytes_recvd, src_addr_len, _, _) = self.do_recvmsg(
&mut buf[..],
flags,
src_addr,
None,
)?;
Ok((bytes_recvd, src_addr_len))
}*/
pub fn recvmsg<'a, 'b>(&self, msg: &'b mut MsgHdrMut<'a>, flags: RecvFlags) -> Result<usize> { pub fn recvmsg<'a, 'b>(&self, msg: &'b mut MsgHdrMut<'a>, flags: RecvFlags) -> Result<usize> {
// Alloc untrusted iovecs to receive data via OCall
let msg_iov = msg.get_iovs();
let u_slice_alloc = UntrustedSliceAlloc::new(msg_iov.total_bytes())?;
let mut u_slices = msg_iov
.as_slices()
.iter()
.map(|slice| {
u_slice_alloc
.new_slice_mut(slice.len())
.expect("unexpected out of memory error in UntrustedSliceAlloc")
})
.collect();
let mut u_iovs = IovsMut::new(u_slices);
// Do OCall-based recvmsg // Do OCall-based recvmsg
let (bytes_recvd, namelen_recvd, controllen_recvd, flags_recvd) = { let (bytes_recvd, namelen_recvd, controllen_recvd, flags_recvd) = {
// Acquire mutable references to the name and control buffers // Acquire mutable references to the name and control buffers
let (name, control) = msg.get_name_and_control_mut(); let (iovs, name, control) = msg.get_iovs_name_and_control_mut();
// Fill the data, the name, and the control buffers // Fill the data, the name, and the control buffers
self.do_recvmsg(u_iovs.as_slices_mut(), flags, name, control)? self.do_recvmsg(iovs.as_slices_mut(), flags, name, control)?
}; };
// Update the output lengths and flags // Update the output lengths and flags
@ -47,19 +21,39 @@ impl SocketFile {
msg.set_control_len(controllen_recvd)?; msg.set_control_len(controllen_recvd)?;
msg.set_flags(flags_recvd); msg.set_flags(flags_recvd);
// Copy data from untrusted iovecs into the output iovecs
let mut msg_iov = msg.get_iovs_mut();
let mut u_iovs_iter = u_iovs
.as_slices()
.iter()
.flat_map(|slice| slice.iter())
.take(bytes_recvd);
msg_iov.copy_from_iter(&mut u_iovs_iter);
Ok(bytes_recvd) Ok(bytes_recvd)
} }
fn do_recvmsg( pub(super) fn do_recvmsg(
&self,
data: &mut [&mut [u8]],
flags: RecvFlags,
mut name: Option<&mut [u8]>,
mut control: Option<&mut [u8]>,
) -> Result<(usize, usize, usize, MsgHdrFlags)> {
let data_length = data.iter().map(|s| s.len()).sum();
let u_allocator = UntrustedSliceAlloc::new(data_length)?;
let mut u_data = {
let mut bufs = Vec::new();
for ref buf in data.iter() {
bufs.push(u_allocator.new_slice_mut(buf.len())?);
}
bufs
};
let retval = self.do_recvmsg_untrusted_data(&mut u_data, flags, name, control)?;
let mut copied = 0;
for (i, buf) in data.iter_mut().enumerate() {
buf.copy_from_slice(u_data[i]);
copied += buf.len();
if copied >= retval.0 {
break;
}
}
Ok(retval)
}
fn do_recvmsg_untrusted_data(
&self, &self,
data: &mut [&mut [u8]], data: &mut [&mut [u8]],
flags: RecvFlags, flags: RecvFlags,
@ -68,7 +62,7 @@ impl SocketFile {
) -> Result<(usize, usize, usize, MsgHdrFlags)> { ) -> Result<(usize, usize, usize, MsgHdrFlags)> {
// Prepare the arguments for OCall // Prepare the arguments for OCall
// Host socket fd // Host socket fd
let host_fd = self.host_fd; let host_fd = self.host_fd();
// Name // Name
let (msg_name, msg_namelen) = name.as_mut_ptr_and_len(); let (msg_name, msg_namelen) = name.as_mut_ptr_and_len();
let msg_name = msg_name as *mut c_void; let msg_name = msg_name as *mut c_void;

@ -1,56 +1,57 @@
use super::*; use super::*;
impl SocketFile { impl HostSocket {
// TODO: need sockaddr type to implement send/sento pub fn send(&self, buf: &[u8], flags: SendFlags) -> Result<usize> {
/* self.sendto(buf, flags, &None)
pub fn send(&self, buf: &[u8], flags: MsgFlags) -> Result<usize> {
self.sendto(buf, flags, None)
} }
pub fn sendto(&self, buf: &[u8], flags: MsgFlags, dest_addr: Option<&[u8]>) -> Result<usize> {
Self::do_sendmsg(
self.host_fd,
&buf[..],
flags,
dest_addr,
None)
}
*/
pub fn sendmsg<'a, 'b>(&self, msg: &'b MsgHdr<'a>, flags: SendFlags) -> Result<usize> { pub fn sendmsg<'a, 'b>(&self, msg: &'b MsgHdr<'a>, flags: SendFlags) -> Result<usize> {
// Copy message's iovecs into untrusted iovecs
let msg_iov = msg.get_iovs(); let msg_iov = msg.get_iovs();
let u_slice_alloc = UntrustedSliceAlloc::new(msg_iov.total_bytes())?;
let u_slices = msg_iov
.as_slices()
.iter()
.map(|src_slice| {
u_slice_alloc
.new_slice(src_slice)
.expect("unexpected out of memory")
})
.collect();
let u_iovs = Iovs::new(u_slices);
self.do_sendmsg(u_iovs.as_slices(), flags, msg.get_name(), msg.get_control()) self.do_sendmsg(
msg_iov.as_slices(),
flags,
msg.get_name(),
msg.get_control(),
)
} }
fn do_sendmsg( pub(super) fn do_sendmsg(
&self, &self,
data: &[&[u8]], data: &[&[u8]],
flags: SendFlags, flags: SendFlags,
name: Option<&[u8]>, name: Option<&[u8]>,
control: Option<&[u8]>, control: Option<&[u8]>,
) -> Result<usize> {
let data_length = data.iter().map(|s| s.len()).sum();
let u_allocator = UntrustedSliceAlloc::new(data_length)?;
let u_data = {
let mut bufs = Vec::new();
for buf in data {
bufs.push(u_allocator.new_slice(buf)?);
}
bufs
};
self.do_sendmsg_untrusted_data(&u_data, flags, name, control)
}
fn do_sendmsg_untrusted_data(
&self,
u_data: &[&[u8]],
flags: SendFlags,
name: Option<&[u8]>,
control: Option<&[u8]>,
) -> Result<usize> { ) -> Result<usize> {
// Prepare the arguments for OCall // Prepare the arguments for OCall
let mut retval: isize = 0; let mut retval: isize = 0;
// Host socket fd // Host socket fd
let host_fd = self.host_fd; let host_fd = self.host_fd();
// Name // Name
let (msg_name, msg_namelen) = name.as_ptr_and_len(); let (msg_name, msg_namelen) = name.as_ptr_and_len();
let msg_name = msg_name as *const c_void; let msg_name = msg_name as *const c_void;
// Iovs // Iovs
let raw_iovs: Vec<libc::iovec> = data.iter().map(|slice| slice.as_libc_iovec()).collect(); let raw_iovs: Vec<libc::iovec> = u_data.iter().map(|slice| slice.as_libc_iovec()).collect();
let (msg_iov, msg_iovlen) = raw_iovs.as_slice().as_ptr_and_len(); let (msg_iov, msg_iovlen) = raw_iovs.as_slice().as_ptr_and_len();
// Control // Control
let (msg_control, msg_controllen) = control.as_ptr_and_len(); let (msg_control, msg_controllen) = control.as_ptr_and_len();
@ -73,7 +74,6 @@ impl SocketFile {
); );
assert!(status == sgx_status_t::SGX_SUCCESS); assert!(status == sgx_status_t::SGX_SUCCESS);
} }
let bytes_sent = if flags.contains(SendFlags::MSG_NOSIGNAL) { let bytes_sent = if flags.contains(SendFlags::MSG_NOSIGNAL) {
try_libc!(retval) try_libc!(retval)
} else { } else {

@ -0,0 +1,77 @@
use super::*;
use crate::fs::{
occlum_ocall_ioctl, AccessMode, CreationFlags, File, FileRef, IoctlCmd, StatusFlags,
};
use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write};
//TODO: refactor write syscall to allow zero length with non-zero buffer
impl File for HostSocket {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.recv(buf, RecvFlags::empty())
}
fn write(&self, buf: &[u8]) -> Result<usize> {
self.send(buf, SendFlags::empty())
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
if offset != 0 {
return_errno!(ESPIPE, "a nonzero position is not supported");
}
self.read(buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
if offset != 0 {
return_errno!(ESPIPE, "a nonzero position is not supported");
}
self.write(buf)
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
let (bytes_recvd, _, _, _) = self.do_recvmsg(bufs, RecvFlags::empty(), None, None)?;
Ok(bytes_recvd)
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
self.do_sendmsg(bufs, SendFlags::empty(), None, None)
}
fn seek(&self, pos: SeekFrom) -> Result<off_t> {
return_errno!(ESPIPE, "Socket does not support seek")
}
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
self.ioctl_impl(cmd)
}
fn get_access_mode(&self) -> Result<AccessMode> {
Ok(AccessMode::O_RDWR)
}
fn get_status_flags(&self) -> Result<StatusFlags> {
let ret = try_libc!(libc::ocall::fcntl_arg0(self.host_fd(), libc::F_GETFL));
Ok(StatusFlags::from_bits_truncate(ret as u32))
}
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
let valid_flags_mask = StatusFlags::O_APPEND
| StatusFlags::O_ASYNC
| StatusFlags::O_DIRECT
| StatusFlags::O_NOATIME
| StatusFlags::O_NONBLOCK;
let raw_status_flags = (new_status_flags & valid_flags_mask).bits();
try_libc!(libc::ocall::fcntl_arg1(
self.host_fd(),
libc::F_SETFL,
raw_status_flags as c_int
));
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}

@ -0,0 +1,19 @@
use super::*;
mod address_family;
mod flags;
mod host_socket;
mod iovs;
mod msg;
mod socket_address;
mod socket_type;
mod unix_socket;
pub use self::address_family::AddressFamily;
pub use self::flags::{FileFlags, MsgHdrFlags, RecvFlags, SendFlags};
pub use self::host_socket::{HostSocket, HostSocketType};
pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec};
pub use self::msg::{msghdr, msghdr_mut, MsgHdr, MsgHdrMut};
pub use self::socket_address::SockAddr;
pub use self::socket_type::SocketType;
pub use self::unix_socket::{AsUnixSocket, UnixSocketFile};

@ -196,8 +196,11 @@ impl<'a> MsgHdrMut<'a> {
Ok(()) Ok(())
} }
pub fn get_name_and_control_mut(&mut self) -> (Option<&mut [u8]>, Option<&mut [u8]>) { pub fn get_iovs_name_and_control_mut(
&mut self,
) -> (&mut IovsMut<'a>, Option<&mut [u8]>, Option<&mut [u8]>) {
( (
&mut self.iovs,
self.name.as_mut().map(|name| &mut name[..]), self.name.as_mut().map(|name| &mut name[..]),
self.control.as_mut().map(|control| &mut control[..]), self.control.as_mut().map(|control| &mut control[..]),
) )

@ -0,0 +1,121 @@
use super::*;
use std::*;
#[derive(Copy, Clone)]
pub struct SockAddr {
storage: libc::sockaddr_storage,
len: usize,
}
// TODO: add more fields
impl fmt::Debug for SockAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SockAddr")
.field(
"family",
&AddressFamily::try_from(self.storage.ss_family).unwrap(),
)
.field("len", &self.len)
.finish()
}
}
impl SockAddr {
// Caller should guarentee the sockaddr and addr_len are valid
pub unsafe fn try_from_raw(
sockaddr: *const libc::sockaddr,
addr_len: libc::socklen_t,
) -> Result<Self> {
if addr_len < std::mem::size_of::<libc::sa_family_t>() as u32 {
return_errno!(EINVAL, "the address is too short.");
}
if addr_len > std::mem::size_of::<libc::sockaddr_storage>() as u32 {
return_errno!(EINVAL, "the address is too long.");
}
match AddressFamily::try_from((*sockaddr).sa_family)? {
AddressFamily::INET => {
if addr_len < std::mem::size_of::<libc::sockaddr_in>() as u32 {
return_errno!(EINVAL, "short ipv4 address.");
}
}
AddressFamily::INET6 => {
let ipv6_addr_len = std::mem::size_of::<libc::sockaddr_in6>() as u32;
// Omit sin6_scope_id when it is not fully provided
// 4 represents the size of sin6_scope_id which is not a must
if addr_len < ipv6_addr_len - 4 {
return_errno!(EINVAL, "wrong ipv6 address length.");
}
}
_ => warn!("address family not checked"),
}
let mut storage = mem::MaybeUninit::<libc::sockaddr_storage>::uninit();
ptr::copy_nonoverlapping(
sockaddr as *const _ as *const u8,
storage.as_mut_ptr() as *mut u8,
addr_len as usize,
);
Ok(Self {
storage: storage.assume_init(),
len: addr_len as usize,
})
}
pub fn as_ptr_and_len(&self) -> (*const libc::sockaddr, usize) {
(self.as_ptr(), self.len())
}
pub fn as_ptr(&self) -> *const libc::sockaddr {
&self.storage as *const _ as *const _
}
pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
&mut self.storage as *mut _ as *mut _
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8, self.len()) }
}
pub fn copy_to_slice(&self, dst: &mut [u8]) -> usize {
let (addr_ptr, addr_len) = self.as_ptr_and_len();
let copy_len = std::cmp::min(addr_len, dst.len());
dst[0..copy_len].copy_from_slice(unsafe {
std::slice::from_raw_parts(addr_ptr as *const u8, copy_len)
});
copy_len
}
pub fn len(&self) -> usize {
self.len
}
pub fn set_len(&mut self, len: usize) -> Result<()> {
if len > Self::capacity() {
return_errno!(EINVAL, "length is too long")
} else {
self.len = len;
Ok(())
}
}
pub fn capacity() -> usize {
mem::size_of::<libc::sockaddr_storage>()
}
}
impl Default for SockAddr {
fn default() -> Self {
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
Self {
storage: storage,
len: mem::size_of::<libc::sockaddr_storage>(),
}
}
}

@ -0,0 +1,29 @@
use super::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
#[allow(non_camel_case_types)]
pub enum SocketType {
STREAM = 1,
DGRAM = 2,
RAW = 3,
RDM = 4,
SEQPACKET = 5,
DCCP = 6,
PACKET = 10,
}
impl SocketType {
pub fn try_from(sock_type: i32) -> Result<Self> {
match sock_type {
1 => Ok(SocketType::STREAM),
2 => Ok(SocketType::DGRAM),
3 => Ok(SocketType::RAW),
4 => Ok(SocketType::RDM),
5 => Ok(SocketType::SEQPACKET),
6 => Ok(SocketType::DCCP),
10 => Ok(SocketType::PACKET),
_ => return_errno!(EINVAL, "invalid socket type"),
}
}
}

@ -1,154 +0,0 @@
use super::*;
use crate::fs::IfConf;
mod ioctl_impl;
mod recv;
mod send;
use fs::{AccessMode, CreationFlags, File, FileRef, IoctlCmd, StatusFlags};
use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write};
/// Native Linux socket
#[derive(Debug)]
pub struct SocketFile {
host_fd: c_int,
}
impl SocketFile {
pub fn new(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<Self> {
let ret = try_libc!(libc::ocall::socket(domain, socket_type, protocol));
Ok(SocketFile { host_fd: ret })
}
pub fn accept(
&self,
addr: *mut libc::sockaddr,
addr_len: *mut libc::socklen_t,
flags: c_int,
) -> Result<Self> {
let ret = try_libc!(libc::ocall::accept4(self.host_fd, addr, addr_len, flags));
Ok(SocketFile { host_fd: ret })
}
pub fn fd(&self) -> c_int {
self.host_fd
}
}
impl Drop for SocketFile {
fn drop(&mut self) {
let ret = unsafe { libc::ocall::close(self.host_fd) };
assert!(ret == 0);
}
}
// TODO: rewrite read/write/readv/writev as send/recv
// TODO: implement readfrom/sendto
impl File for SocketFile {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let (buf_ptr, buf_len) = buf.as_mut().as_mut_ptr_and_len();
let ret = try_libc!(libc::ocall::read(
self.host_fd,
buf_ptr as *mut c_void,
buf_len
)) as usize;
assert!(ret <= buf_len);
Ok(ret)
}
fn write(&self, buf: &[u8]) -> Result<usize> {
let (buf_ptr, buf_len) = buf.as_ptr_and_len();
let ret = try_libc_may_epipe!(libc::ocall::write(
self.host_fd,
buf_ptr as *const c_void,
buf_len
)) as usize;
assert!(ret <= buf_len);
Ok(ret)
}
fn read_at(&self, _offset: usize, buf: &mut [u8]) -> Result<usize> {
self.read(buf)
}
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
self.write(buf)
}
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
let mut total_len = 0;
for buf in bufs {
match self.read(buf) {
Ok(len) => {
total_len += len;
}
Err(_) if total_len != 0 => break,
Err(e) => return Err(e.into()),
}
}
Ok(total_len)
}
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
let mut total_len = 0;
for buf in bufs {
match self.write(buf) {
Ok(len) => {
total_len += len;
}
Err(_) if total_len != 0 => break,
Err(e) => return Err(e.into()),
}
}
Ok(total_len)
}
fn seek(&self, pos: SeekFrom) -> Result<off_t> {
return_errno!(ESPIPE, "Socket does not support seek")
}
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
self.ioctl_impl(cmd)
}
fn get_access_mode(&self) -> Result<AccessMode> {
Ok(AccessMode::O_RDWR)
}
fn get_status_flags(&self) -> Result<StatusFlags> {
let ret = try_libc!(libc::ocall::fcntl_arg0(self.fd(), libc::F_GETFL));
Ok(StatusFlags::from_bits_truncate(ret as u32))
}
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
let valid_flags_mask = StatusFlags::O_APPEND
| StatusFlags::O_ASYNC
| StatusFlags::O_DIRECT
| StatusFlags::O_NOATIME
| StatusFlags::O_NONBLOCK;
let raw_status_flags = (new_status_flags & valid_flags_mask).bits();
try_libc!(libc::ocall::fcntl_arg1(
self.fd(),
libc::F_SETFL,
raw_status_flags as c_int
));
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub trait AsSocket {
fn as_socket(&self) -> Result<&SocketFile>;
}
impl AsSocket for FileRef {
fn as_socket(&self) -> Result<&SocketFile> {
self.as_any()
.downcast_ref::<SocketFile>()
.ok_or_else(|| errno!(EBADF, "not a socket"))
}
}

@ -9,50 +9,87 @@ use time::timeval_t;
use util::mem_util::from_user; use util::mem_util::from_user;
pub fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize> { pub fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize> {
debug!( let sock_domain = AddressFamily::try_from(domain as u16)?;
"socket: domain: {}, socket_type: 0x{:x}, protocol: {}", let file_flags = FileFlags::from_bits_truncate(socket_type);
domain, socket_type, protocol let sock_type = SocketType::try_from(socket_type & (!file_flags.bits()))?;
);
let file_ref: Arc<Box<dyn File>> = match domain { let file_ref: Arc<Box<dyn File>> = match sock_domain {
libc::AF_LOCAL => { AddressFamily::LOCAL => {
let unix_socket = UnixSocketFile::new(socket_type, protocol)?; let unix_socket = UnixSocketFile::new(socket_type, protocol)?;
Arc::new(Box::new(unix_socket)) Arc::new(Box::new(unix_socket))
} }
_ => { _ => {
let socket = SocketFile::new(domain, socket_type, protocol)?; let socket = HostSocket::new(sock_domain, sock_type, file_flags, protocol)?;
Arc::new(Box::new(socket)) Arc::new(Box::new(socket))
} }
}; };
let fd = current!().add_file(file_ref, false); let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
let fd = current!().add_file(file_ref, close_on_spawn);
Ok(fd as isize) Ok(fd as isize)
} }
pub fn do_bind(fd: c_int, addr: *const libc::sockaddr, addr_len: libc::socklen_t) -> Result<isize> {
if addr.is_null() || addr_len == 0 {
return_errno!(EINVAL, "no address is specified");
}
from_user::check_array(addr as *const u8, addr_len as usize)?;
let sock_addr = unsafe { SockAddr::try_from_raw(addr, addr_len)? };
trace!("bind to addr: {:?}", sock_addr);
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_host_socket() {
socket.bind(&sock_addr)?;
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
let addr = addr as *const libc::sockaddr_un;
from_user::check_ptr(addr)?;
let path = from_user::clone_cstring_safely(unsafe { (&*addr).sun_path.as_ptr() })?
.to_string_lossy()
.into_owned();
unix_socket.bind(path)?;
} else {
return_errno!(EBADF, "not a socket");
}
Ok(0)
}
pub fn do_listen(fd: c_int, backlog: c_int) -> Result<isize> {
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_host_socket() {
socket.listen(backlog)?;
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
unix_socket.listen()?;
} else {
return_errno!(EBADF, "not a socket");
}
Ok(0)
}
pub fn do_connect( pub fn do_connect(
fd: c_int, fd: c_int,
addr: *const libc::sockaddr, addr: *const libc::sockaddr,
addr_len: libc::socklen_t, addr_len: libc::socklen_t,
) -> Result<isize> { ) -> Result<isize> {
debug!(
"connect: fd: {}, addr: {:?}, addr_len: {}",
fd, addr, addr_len
);
// For SOCK_DGRAM sockets not initiated in connection-mode, // For SOCK_DGRAM sockets not initiated in connection-mode,
// if address is a null address for the protocol, // if address is a null address for the protocol,
// the socket's peer address shall be reset. // the socket's peer address shall be reset.
let need_check: bool = !addr.is_null(); let addr_set: bool = !addr.is_null();
if need_check { if addr_set {
from_user::check_array(addr as *const u8, addr_len as usize)?; from_user::check_array(addr as *const u8, addr_len as usize)?;
} }
let addr_option = if addr_set {
Some(unsafe { SockAddr::try_from_raw(addr, addr_len)? })
} else {
None
};
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
if need_check { socket.connect(&addr_option)?;
from_user::check_ptr(addr as *const libc::sockaddr_in)?;
}
let ret = try_libc!(libc::ocall::connect(socket.fd(), addr, addr_len));
Ok(ret as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() { } else if let Ok(unix_socket) = file_ref.as_unix_socket() {
let addr = addr as *const libc::sockaddr_un; let addr = addr as *const libc::sockaddr_un;
from_user::check_ptr(addr)?; from_user::check_ptr(addr)?;
@ -60,10 +97,11 @@ pub fn do_connect(
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
unix_socket.connect(path)?; unix_socket.connect(path)?;
Ok(0)
} else { } else {
return_errno!(EBADF, "not a socket") return_errno!(EBADF, "not a socket")
} }
Ok(0)
} }
pub fn do_accept( pub fn do_accept(
@ -80,93 +118,55 @@ pub fn do_accept4(
addr_len: *mut libc::socklen_t, addr_len: *mut libc::socklen_t,
flags: c_int, flags: c_int,
) -> Result<isize> { ) -> Result<isize> {
debug!( let addr_set: bool = !addr.is_null();
"accept4: fd: {}, addr: {:?}, addr_len: {:?}, flags: {:#x}", if addr_set {
fd, addr, addr_len, flags from_user::check_ptr(addr_len)?;
);
let need_check: bool = !addr.is_null();
if addr.is_null() ^ addr_len.is_null() {
return_errno!(EINVAL, "addr and ddr_len should be both null");
}
if need_check {
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?; from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
} }
let file_flags = FileFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { let new_fd = if let Ok(socket) = file_ref.as_host_socket() {
if need_check { let (new_socket_file, sock_addr_option) = socket.accept(file_flags)?;
from_user::check_mut_ptr(addr as *mut libc::sockaddr_in)?; let new_file_ref: Arc<Box<dyn File>> = Arc::new(Box::new(new_socket_file));
let new_fd = current!().add_file(new_file_ref, close_on_spawn);
if addr_set && sock_addr_option.is_some() {
let sock_addr = sock_addr_option.unwrap();
let mut buf =
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
sock_addr.copy_to_slice(&mut buf);
unsafe {
*addr_len = sock_addr.len() as u32;
} }
}
let new_socket = socket.accept(addr, addr_len, flags)?; new_fd
let new_file_ref: Arc<Box<dyn File>> = Arc::new(Box::new(new_socket));
let new_fd = current!().add_file(new_file_ref, false);
Ok(new_fd as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() { } else if let Ok(unix_socket) = file_ref.as_unix_socket() {
let addr = addr as *mut libc::sockaddr_un; let addr = addr as *mut libc::sockaddr_un;
if need_check { if addr_set {
from_user::check_mut_ptr(addr)?; from_user::check_mut_ptr(addr)?;
} }
// TODO: handle addr // TODO: handle addr
let new_socket = unix_socket.accept()?; let new_socket = unix_socket.accept()?;
let new_file_ref: Arc<Box<dyn File>> = Arc::new(Box::new(new_socket)); let new_file_ref: Arc<Box<dyn File>> = Arc::new(Box::new(new_socket));
let new_fd = current!().add_file(new_file_ref, false); current!().add_file(new_file_ref, false)
} else {
return_errno!(EBADF, "not a socket");
};
Ok(new_fd as isize) Ok(new_fd as isize)
} else {
return_errno!(EBADF, "not a socket")
}
} }
pub fn do_shutdown(fd: c_int, how: c_int) -> Result<isize> { pub fn do_shutdown(fd: c_int, how: c_int) -> Result<isize> {
debug!("shutdown: fd: {}, how: {}", fd, how); debug!("shutdown: fd: {}, how: {}", fd, how);
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let ret = try_libc!(libc::ocall::shutdown(socket.fd(), how)); let ret = try_libc!(libc::ocall::shutdown(socket.host_fd(), how));
Ok(ret as isize) Ok(ret as isize)
} else { } else {
return_errno!(EBADF, "not a socket") // TODO: support unix socket
}
}
pub fn do_bind(fd: c_int, addr: *const libc::sockaddr, addr_len: libc::socklen_t) -> Result<isize> {
debug!("bind: fd: {}, addr: {:?}, addr_len: {}", fd, addr, addr_len);
if addr.is_null() && addr_len == 0 {
return_errno!(EINVAL, "no address is specified");
}
from_user::check_array(addr as *const u8, addr_len as usize)?;
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() {
from_user::check_ptr(addr as *const libc::sockaddr_in)?;
let ret = try_libc!(libc::ocall::bind(socket.fd(), addr, addr_len));
Ok(ret as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
let addr = addr as *const libc::sockaddr_un;
from_user::check_ptr(addr)?;
let path = from_user::clone_cstring_safely(unsafe { (&*addr).sun_path.as_ptr() })?
.to_string_lossy()
.into_owned();
unix_socket.bind(path)?;
Ok(0)
} else {
return_errno!(EBADF, "not a socket")
}
}
pub fn do_listen(fd: c_int, backlog: c_int) -> Result<isize> {
debug!("listen: fd: {}, backlog: {}", fd, backlog);
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() {
let ret = try_libc!(libc::ocall::listen(socket.fd(), backlog));
Ok(ret as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
unix_socket.listen()?;
Ok(0)
} else {
return_errno!(EBADF, "not a socket") return_errno!(EBADF, "not a socket")
} }
} }
@ -183,9 +183,9 @@ pub fn do_setsockopt(
fd, level, optname, optval, optlen fd, level, optname, optval, optlen
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let ret = try_libc!(libc::ocall::setsockopt( let ret = try_libc!(libc::ocall::setsockopt(
socket.fd(), socket.host_fd(),
level, level,
optname, optname,
optval, optval,
@ -212,10 +212,10 @@ pub fn do_getsockopt(
fd, level, optname, optval, optlen fd, level, optname, optval, optlen
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
let socket = file_ref.as_socket()?; let socket = file_ref.as_host_socket()?;
let ret = try_libc!(libc::ocall::getsockopt( let ret = try_libc!(libc::ocall::getsockopt(
socket.fd(), socket.host_fd(),
level, level,
optname, optname,
optval, optval,
@ -234,8 +234,8 @@ pub fn do_getpeername(
fd, addr, addr_len fd, addr, addr_len
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let ret = try_libc!(libc::ocall::getpeername(socket.fd(), addr, addr_len)); let ret = try_libc!(libc::ocall::getpeername(socket.host_fd(), addr, addr_len));
Ok(ret as isize) Ok(ret as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() { } else if let Ok(unix_socket) = file_ref.as_unix_socket() {
warn!("getpeername for unix socket is unimplemented"); warn!("getpeername for unix socket is unimplemented");
@ -258,8 +258,8 @@ pub fn do_getsockname(
fd, addr, addr_len fd, addr, addr_len
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let ret = try_libc!(libc::ocall::getsockname(socket.fd(), addr, addr_len)); let ret = try_libc!(libc::ocall::getsockname(socket.host_fd(), addr, addr_len));
Ok(ret as isize) Ok(ret as isize)
} else if let Ok(unix_socket) = file_ref.as_unix_socket() { } else if let Ok(unix_socket) = file_ref.as_unix_socket() {
warn!("getsockname for unix socket is unimplemented"); warn!("getsockname for unix socket is unimplemented");
@ -277,29 +277,36 @@ pub fn do_sendto(
addr: *const libc::sockaddr, addr: *const libc::sockaddr,
addr_len: libc::socklen_t, addr_len: libc::socklen_t,
) -> Result<isize> { ) -> Result<isize> {
debug!( if len == 0 {
"sendto: fd: {}, base: {:?}, len: {}, flags: {} addr: {:?}, addr_len: {}", return Ok(0);
fd, base, len, flags, addr, addr_len
);
from_user::check_array(base as *const u8, len)?;
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() {
// TODO: check addr and addr_len according to connection mode
let ret = try_libc_may_epipe!(libc::ocall::sendto(
socket.fd(),
base,
len,
flags,
addr,
addr_len
));
Ok(ret as isize)
} else if let Ok(unix) = file_ref.as_unix_socket() {
if !addr.is_null() || addr_len != 0 {
return_errno!(EISCONN, "Only connection-mode socket is supported");
} }
if addr.is_null() ^ (addr_len == 0) {
return_errno!(EINVAL, "addr and ddr_len should be both null");
}
from_user::check_array(base as *const u8, len)?;
let buf = unsafe { std::slice::from_raw_parts(base as *const u8, len as usize) };
let addr_set: bool = !addr.is_null();
if addr_set {
from_user::check_mut_array(addr as *mut u8, addr_len as usize)?;
}
let send_flags = SendFlags::from_bits(flags).unwrap();
let addr_option = if addr_set {
Some(unsafe { SockAddr::try_from_raw(addr, addr_len)? })
} else {
None
};
let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_host_socket() {
socket
.sendto(buf, send_flags, &addr_option)
.map(|u| u as isize)
} else if let Ok(unix) = file_ref.as_unix_socket() {
if !unix.is_connected() { if !unix.is_connected() {
return_errno!(ENOTCONN, "the socket has not been connected yet"); return_errno!(ENOTCONN, "the socket has not been connected yet");
} }
@ -319,22 +326,42 @@ pub fn do_recvfrom(
addr: *mut libc::sockaddr, addr: *mut libc::sockaddr,
addr_len: *mut libc::socklen_t, addr_len: *mut libc::socklen_t,
) -> Result<isize> { ) -> Result<isize> {
debug!( if addr.is_null() ^ addr_len.is_null() {
"recvfrom: fd: {}, base: {:?}, len: {}, flags: {}, addr: {:?}, addr_len: {:?}", return_errno!(EINVAL, "addr and ddr_len should be both null");
fd, base, len, flags, addr, addr_len }
);
let file_ref = current!().file(fd as FileDesc)?;
let socket = file_ref.as_socket()?;
let ret = try_libc!(libc::ocall::recvfrom( from_user::check_array(base as *mut u8, len)?;
socket.fd(), let mut buf = unsafe { std::slice::from_raw_parts_mut(base as *mut u8, len as usize) };
base,
len, // MSG_CTRUNC is a return flag but linux allows it to be set on input flags.
flags, // We just ignore it.
addr, let recv_flags = RecvFlags::from_bits(flags & !(MsgHdrFlags::MSG_CTRUNC.bits()))
addr_len .ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
));
Ok(ret as isize) let addr_set: bool = !addr.is_null();
if addr_set {
from_user::check_ptr(addr_len)?;
from_user::check_mut_array(addr as *mut u8, unsafe { *addr_len } as usize)?;
}
let file_ref = current!().file(fd as FileDesc)?;
let (data_len, sock_addr_option) = if let Ok(socket) = file_ref.as_host_socket() {
socket.recvfrom(buf, recv_flags)?
} else {
return_errno!(EBADF, "not a socket");
};
if addr_set && sock_addr_option.is_some() {
let sock_addr = sock_addr_option.unwrap();
let mut buf =
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, *addr_len as usize) };
sock_addr.copy_to_slice(&mut buf);
unsafe {
*addr_len = sock_addr.len() as u32;
}
}
Ok(data_len as isize)
} }
pub fn do_socketpair( pub fn do_socketpair(
@ -343,27 +370,26 @@ pub fn do_socketpair(
protocol: c_int, protocol: c_int,
sv: *mut c_int, sv: *mut c_int,
) -> Result<isize> { ) -> Result<isize> {
debug!(
"socketpair: domain: {}, type:0x{:x}, protocol: {}",
domain, socket_type, protocol
);
let mut sock_pair = unsafe { let mut sock_pair = unsafe {
from_user::check_mut_array(sv, 2)?; from_user::check_mut_array(sv, 2)?;
std::slice::from_raw_parts_mut(sv as *mut u32, 2) std::slice::from_raw_parts_mut(sv as *mut u32, 2)
}; };
if (domain == libc::AF_UNIX) { let file_flags = FileFlags::from_bits_truncate(socket_type);
let close_on_spawn = file_flags.contains(FileFlags::SOCK_CLOEXEC);
let domain = AddressFamily::try_from(domain as u16)?;
if (domain == AddressFamily::LOCAL) {
let (client_socket, server_socket) = let (client_socket, server_socket) =
UnixSocketFile::socketpair(socket_type as i32, protocol as i32)?; UnixSocketFile::socketpair(socket_type as i32, protocol as i32)?;
let current = current!(); let current = current!();
let mut files = current.files().lock().unwrap(); let mut files = current.files().lock().unwrap();
sock_pair[0] = files.put(Arc::new(Box::new(client_socket)), false); sock_pair[0] = files.put(Arc::new(Box::new(client_socket)), close_on_spawn);
sock_pair[1] = files.put(Arc::new(Box::new(server_socket)), false); sock_pair[1] = files.put(Arc::new(Box::new(server_socket)), close_on_spawn);
debug!("socketpair: ({}, {})", sock_pair[0], sock_pair[1]); debug!("socketpair: ({}, {})", sock_pair[0], sock_pair[1]);
Ok(0) Ok(0)
} else if (domain == libc::AF_TIPC) {
return_errno!(EAFNOSUPPORT, "cluster domain sockets not supported")
} else { } else {
return_errno!(EAFNOSUPPORT, "domain not supported") return_errno!(EAFNOSUPPORT, "domain not supported")
} }
@ -376,7 +402,7 @@ pub fn do_sendmsg(fd: c_int, msg_ptr: *const msghdr, flags_c: c_int) -> Result<i
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let msg_c = { let msg_c = {
from_user::check_ptr(msg_ptr)?; from_user::check_ptr(msg_ptr)?;
let msg_c = unsafe { &*msg_ptr }; let msg_c = unsafe { &*msg_ptr };
@ -404,7 +430,7 @@ pub fn do_recvmsg(fd: c_int, msg_mut_ptr: *mut msghdr_mut, flags_c: c_int) -> Re
); );
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
if let Ok(socket) = file_ref.as_socket() { if let Ok(socket) = file_ref.as_host_socket() {
let msg_mut_c = { let msg_mut_c = {
from_user::check_mut_ptr(msg_mut_ptr)?; from_user::check_mut_ptr(msg_mut_ptr)?;
let msg_mut_c = unsafe { &mut *msg_mut_ptr }; let msg_mut_c = unsafe { &mut *msg_mut_ptr };

@ -37,8 +37,7 @@ use crate::net::{
do_accept, do_accept4, do_bind, do_connect, do_epoll_create, do_epoll_create1, do_epoll_ctl, do_accept, do_accept4, do_bind, do_connect, do_epoll_create, do_epoll_create1, do_epoll_ctl,
do_epoll_pwait, do_epoll_wait, do_getpeername, do_getsockname, do_getsockopt, do_listen, do_epoll_pwait, do_epoll_wait, do_getpeername, do_getsockname, do_getsockopt, do_listen,
do_poll, do_recvfrom, do_recvmsg, do_select, do_sendmsg, do_sendto, do_setsockopt, do_shutdown, do_poll, do_recvfrom, do_recvmsg, do_select, do_sendmsg, do_sendto, do_setsockopt, do_shutdown,
do_socket, do_socketpair, msghdr, msghdr_mut, AsSocket, AsUnixSocket, EpollEvent, PollEvent, do_socket, do_socketpair, msghdr, msghdr_mut, PollEvent,
SocketFile, UnixSocketFile,
}; };
use crate::process::{ use crate::process::{
do_arch_prctl, do_clone, do_exit, do_exit_group, do_futex, do_getegid, do_geteuid, do_getgid, do_arch_prctl, do_clone, do_exit, do_exit_group, do_futex, do_getegid, do_geteuid, do_getgid,