[libos] Re-implement unix/host socket and unify net-related structs

This commit is contained in:
ClawSeven 2024-04-30 14:20:49 +08:00 committed by volcano
parent f91cd60786
commit 92ec7c334b
34 changed files with 1315 additions and 896 deletions

@ -14,6 +14,9 @@ bitflags! {
const HUP = 0x0010; // = POLLHUP const HUP = 0x0010; // = POLLHUP
const NVAL = 0x0020; // = POLLNVAL const NVAL = 0x0020; // = POLLNVAL
const RDHUP = 0x2000; // = POLLRDHUP const RDHUP = 0x2000; // = POLLRDHUP
/// Events that are always polled even without specifying them.
const ALWAYS_POLL = Self::ERR.bits | Self::HUP.bits;
} }
} }

@ -129,4 +129,8 @@ impl StatusFlags {
pub fn is_fast_open(&self) -> bool { pub fn is_fast_open(&self) -> bool {
self.contains(StatusFlags::O_PATH) self.contains(StatusFlags::O_PATH)
} }
pub fn is_nonblocking(&self) -> bool {
self.contains(StatusFlags::O_NONBLOCK)
}
} }

@ -99,7 +99,7 @@ fn get_ifconf_by_host(fd: FileDesc, if_conf: &mut IfConf) -> Result<()> {
// len: the size of the buf // len: the size of the buf
// recv_len: accepts transferred data length when buf is used to get data from host // recv_len: accepts transferred data length when buf is used to get data from host
// //
fn socket_ocall_ioctl_repack( fn occlum_ocall_ioctl_repack(
ret: *mut i32, ret: *mut i32,
fd: i32, fd: i32,
cmd_num: i32, cmd_num: i32,
@ -112,7 +112,7 @@ fn get_ifconf_by_host(fd: FileDesc, if_conf: &mut IfConf) -> Result<()> {
try_libc!({ try_libc!({
let mut recv_len: i32 = 0; let mut recv_len: i32 = 0;
let mut retval: i32 = 0; let mut retval: i32 = 0;
let status = socket_ocall_ioctl_repack( let status = occlum_ocall_ioctl_repack(
&mut retval as *mut i32, &mut retval as *mut i32,
fd as _, fd as _,
SIOCGIFCONF as _, SIOCGIFCONF as _,

@ -1,10 +1,9 @@
use std::cell::Cell;
use std::ptr; use std::ptr;
use std::sync::Weak; use std::sync::Weak;
use std::time::Duration; use std::time::Duration;
use crate::events::{Observer, Waiter, WaiterQueueObserver}; use crate::events::{Observer, Waiter, WaiterQueueObserver};
use crate::fs::{AtomicIoEvents, IoEvents}; use crate::fs::IoEvents;
use crate::prelude::*; use crate::prelude::*;
use crate::time::{timespec_t, TIMERSLACK}; use crate::time::{timespec_t, TIMERSLACK};
@ -265,14 +264,14 @@ trait ObserverExt {
impl ObserverExt for Weak<dyn Observer<IoEvents>> { impl ObserverExt for Weak<dyn Observer<IoEvents>> {
fn register_files<'a>(&self, files_and_events: impl Iterator<Item = &'a (FileRef, IoEvents)>) { fn register_files<'a>(&self, files_and_events: impl Iterator<Item = &'a (FileRef, IoEvents)>) {
for (file, events) in files_and_events { for (file, events) in files_and_events {
let notifier = match file.notifier() { match file.notifier() {
None => continue, None => continue,
Some(notifier) => notifier, Some(notifier) => {
};
let mask = *events; let mask = *events;
notifier.register(self.clone(), Some(mask), None); notifier.register(self.clone(), Some(mask), None);
} }
};
}
} }
fn unregister_files<'a>( fn unregister_files<'a>(
@ -280,11 +279,12 @@ impl ObserverExt for Weak<dyn Observer<IoEvents>> {
files_and_events: impl Iterator<Item = &'a (FileRef, IoEvents)>, files_and_events: impl Iterator<Item = &'a (FileRef, IoEvents)>,
) { ) {
for (file, events) in files_and_events { for (file, events) in files_and_events {
let notifier = match file.notifier() { match file.notifier() {
None => continue, None => continue,
Some(notifier) => notifier, Some(notifier) => {
};
notifier.unregister(self); notifier.unregister(self);
} }
};
}
} }
} }

@ -1,43 +0,0 @@
use super::*;
bitflags! {
pub struct SendFlags: i32 {
const MSG_OOB = 0x01;
const MSG_DONTROUTE = 0x04;
const MSG_DONTWAIT = 0x40; // Nonblocking io
const MSG_EOR = 0x80; // End of record
const MSG_CONFIRM = 0x0800; // Confirm path validity
const MSG_NOSIGNAL = 0x4000; // Do not generate SIGPIPE
const MSG_MORE = 0x8000; // Sender will send more
}
}
bitflags! {
pub struct RecvFlags: i32 {
const MSG_OOB = 0x01;
const MSG_PEEK = 0x02;
const MSG_TRUNC = 0x20;
const MSG_DONTWAIT = 0x40; // Nonblocking io
const MSG_WAITALL = 0x0100; // Wait for a full request
const MSG_ERRQUEUE = 0x2000; // Fetch message from error queue
const MSG_CMSG_CLOEXEC = 0x40000000; // Set close_on_exec for file descriptor received through M_RIGHTS
}
}
bitflags! {
pub struct MsgHdrFlags: i32 {
const MSG_OOB = 0x01;
const MSG_CTRUNC = 0x08;
const MSG_TRUNC = 0x20;
const MSG_EOR = 0x80; // End of record
const MSG_ERRQUEUE = 0x2000; // Fetch message from error queue
const MSG_NOTIFICATION = 0x8000; // Only applicable to SCTP socket
}
}
bitflags! {
pub struct FileFlags: i32 {
const SOCK_NONBLOCK = 0x800;
const SOCK_CLOEXEC = 0x80000;
}
}

@ -1,82 +0,0 @@
use super::*;
use fs::{occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd};
impl HostSocket {
pub(super) fn ioctl_impl(&self, cmd: &mut IoctlCmd) -> Result<i32> {
if let IoctlCmd::SIOCGIFCONF(arg_ref) = cmd {
return self.ioctl_getifconf(arg_ref);
}
let cmd_num = cmd.cmd_num() as c_int;
let cmd_arg_ptr = cmd.arg_ptr() as *mut c_void;
let ret = try_libc!({
let mut retval: i32 = 0;
let status = occlum_ocall_ioctl(
&mut retval as *mut i32,
self.raw_host_fd() as i32,
cmd_num,
cmd_arg_ptr,
cmd.arg_len(),
);
assert!(status == sgx_status_t::SGX_SUCCESS);
retval
});
// FIXME: add sanity checks for results returned for socket-related ioctls
cmd.validate_arg_and_ret_vals(ret)?;
Ok(ret)
}
fn ioctl_getifconf(&self, arg_ref: &mut IfConf) -> Result<i32> {
if !arg_ref.ifc_buf.is_null() && arg_ref.ifc_len == 0 {
return Ok(0);
}
let ret = try_libc!({
let mut recv_len: i32 = 0;
let mut retval: i32 = 0;
let status = occlum_ocall_ioctl_repack(
&mut retval as *mut i32,
self.raw_host_fd() as i32,
BuiltinIoctlNum::SIOCGIFCONF as i32,
arg_ref.ifc_buf,
arg_ref.ifc_len,
&mut recv_len as *mut i32,
);
assert!(status == sgx_status_t::SGX_SUCCESS);
// If ifc_req is NULL, SIOCGIFCONF returns the necessary buffer
// size in bytes for receiving all available addresses in ifc_len
// which is irrelevant to the orginal ifc_len.
if !arg_ref.ifc_buf.is_null() {
assert!(arg_ref.ifc_len >= recv_len);
}
arg_ref.ifc_len = recv_len;
retval
});
Ok(ret)
}
}
extern "C" {
// Used to ioctl arguments with pointer members.
//
// Before the call the area the pointers points to should be assembled into
// one continous memory block. Then the block is repacked to ioctl arguments
// in the ocall implementation in host.
//
// ret: holds the return value of ioctl in host
// fd: the host fd for the device
// cmd_num: request number of the ioctl
// buf: the data to exchange with host
// len: the size of the buf
// recv_len: accepts transferred data length when buf is used to get data from host
//
fn occlum_ocall_ioctl_repack(
ret: *mut i32,
fd: c_int,
cmd_num: c_int,
buf: *const u8,
len: i32,
recv_len: *mut i32,
) -> sgx_status_t;
}

@ -1,18 +1,16 @@
use std::any::Any; use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use std::mem; use std::mem;
use atomic::Atomic; use atomic::Atomic;
use super::*; use super::*;
use crate::fs::{ use crate::fs::{
occlum_ocall_ioctl, AccessMode, CreationFlags, File, FileRef, HostFd, IoEvents, IoNotifier, occlum_ocall_ioctl, AccessMode, CreationFlags, File, FileRef, HostFd, IoEvents, IoNotifier,
IoctlCmd, StatusFlags, IoctlRawCmd, StatusFlags,
}; };
use crate::process::IO_BUF_SIZE; use crate::process::IO_BUF_SIZE;
mod ioctl_impl;
mod recv; mod recv;
mod send; mod send;
mod socket_file; mod socket_file;
@ -27,14 +25,14 @@ pub struct HostSocket {
impl HostSocket { impl HostSocket {
pub fn new( pub fn new(
domain: AddressFamily, domain: Domain,
socket_type: SocketType, socket_type: Type,
file_flags: FileFlags, socket_flags: SocketFlags,
protocol: i32, protocol: i32,
) -> Result<Self> { ) -> Result<Self> {
let raw_host_fd = try_libc!(libc::ocall::socket( let raw_host_fd = try_libc!(libc::ocall::socket(
domain as i32, domain as i32,
socket_type as i32 | file_flags.bits(), socket_type as i32 | socket_flags.bits(),
protocol protocol
)) as FileDesc; )) as FileDesc;
let host_fd = HostFd::new(raw_host_fd); let host_fd = HostFd::new(raw_host_fd);
@ -51,7 +49,7 @@ impl HostSocket {
}) })
} }
pub fn bind(&self, addr: &SockAddr) -> Result<()> { pub fn bind(&self, addr: &RawAddr) -> Result<()> {
let (addr_ptr, addr_len) = addr.as_ptr_and_len(); let (addr_ptr, addr_len) = addr.as_ptr_and_len();
let ret = try_libc!(libc::ocall::bind( let ret = try_libc!(libc::ocall::bind(
@ -67,8 +65,8 @@ impl HostSocket {
Ok(()) Ok(())
} }
pub fn accept(&self, flags: FileFlags) -> Result<(Self, Option<SockAddr>)> { pub fn accept(&self, flags: SocketFlags) -> Result<(Self, Option<RawAddr>)> {
let mut sockaddr = SockAddr::default(); let mut sockaddr = RawAddr::default();
let mut addr_len = sockaddr.len(); let mut addr_len = sockaddr.len();
let raw_host_fd = try_libc!(libc::ocall::accept4( let raw_host_fd = try_libc!(libc::ocall::accept4(
@ -88,7 +86,33 @@ impl HostSocket {
Ok((HostSocket::from_host_fd(host_fd)?, addr_option)) Ok((HostSocket::from_host_fd(host_fd)?, addr_option))
} }
pub fn connect(&self, addr: &Option<SockAddr>) -> Result<()> { pub fn addr(&self) -> Result<RawAddr> {
let mut sockaddr = RawAddr::default();
let mut addr_len = sockaddr.len();
try_libc!(libc::ocall::getsockname(
self.raw_host_fd() as i32,
sockaddr.as_mut_ptr() as *mut _,
&mut addr_len as *mut _ as *mut _,
));
sockaddr.set_len(addr_len)?;
Ok(sockaddr)
}
pub fn peer_addr(&self) -> Result<RawAddr> {
let mut sockaddr = RawAddr::default();
let mut addr_len = sockaddr.len();
try_libc!(libc::ocall::getpeername(
self.raw_host_fd() as i32,
sockaddr.as_mut_ptr() as *mut _,
&mut addr_len as *mut _ as *mut _,
));
sockaddr.set_len(addr_len)?;
Ok(sockaddr)
}
pub fn connect(&self, addr: &Option<RawAddr>) -> Result<()> {
debug!("connect: host_fd: {}, addr {:?}", self.raw_host_fd(), addr); debug!("connect: host_fd: {}, addr {:?}", self.raw_host_fd(), addr);
let (addr_ptr, addr_len) = if let Some(sock_addr) = addr { let (addr_ptr, addr_len) = if let Some(sock_addr) = addr {
@ -109,34 +133,29 @@ impl HostSocket {
&self, &self,
buf: &[u8], buf: &[u8],
flags: SendFlags, flags: SendFlags,
addr_option: &Option<SockAddr>, addr_option: &Option<RawAddr>,
) -> Result<usize> { ) -> Result<usize> {
let bufs = vec![buf]; let bufs = vec![buf];
let name_option = addr_option.as_ref().map(|addr| addr.as_slice()); self.sendmsg(&bufs, flags, addr_option, None)
self.do_sendmsg(&bufs, flags, name_option, None)
} }
pub fn recvfrom(&self, buf: &mut [u8], flags: RecvFlags) -> Result<(usize, Option<SockAddr>)> { pub fn recvfrom(&self, buf: &mut [u8], flags: RecvFlags) -> Result<(usize, Option<RawAddr>)> {
let mut sockaddr = SockAddr::default(); let mut sockaddr = RawAddr::default();
let mut bufs = vec![buf]; let mut bufs = vec![buf];
let (bytes_recv, addr_len, _, _) = let (bytes_recv, recv_addr, _, _) = self.recvmsg(&mut bufs, flags, None)?;
self.do_recvmsg(&mut bufs, flags, Some(sockaddr.as_mut_slice()), None)?;
let addr_option = if addr_len != 0 { Ok((bytes_recv, recv_addr))
sockaddr.set_len(addr_len)?;
Some(sockaddr)
} else {
None
};
Ok((bytes_recv, addr_option))
} }
pub fn raw_host_fd(&self) -> FileDesc { pub fn raw_host_fd(&self) -> FileDesc {
self.host_fd.to_raw() self.host_fd.to_raw()
} }
pub fn shutdown(&self, how: HowToShut) -> Result<()> { pub fn shutdown(&self, how: Shutdown) -> Result<()> {
try_libc!(libc::ocall::shutdown(self.raw_host_fd() as i32, how.bits())); try_libc!(libc::ocall::shutdown(
self.raw_host_fd() as i32,
how.to_c() as i32
));
Ok(()) Ok(())
} }
} }

@ -7,30 +7,12 @@ impl HostSocket {
Ok(bytes_recvd) Ok(bytes_recvd)
} }
pub fn recvmsg<'a, 'b>(&self, msg: &'b mut MsgHdrMut<'a>, flags: RecvFlags) -> Result<usize> { pub fn recvmsg(
// Do OCall-based recvmsg
let (bytes_recvd, namelen_recvd, controllen_recvd, flags_recvd) = {
// Acquire mutable references to the name and control buffers
let (iovs, name, control) = msg.get_iovs_name_and_control_mut();
// Fill the data, the name, and the control buffers
self.do_recvmsg(iovs.as_slices_mut(), flags, name, control)?
};
// Update the output lengths and flags
msg.set_name_len(namelen_recvd)?;
msg.set_control_len(controllen_recvd)?;
msg.set_flags(flags_recvd);
Ok(bytes_recvd)
}
pub(super) fn do_recvmsg(
&self, &self,
data: &mut [&mut [u8]], data: &mut [&mut [u8]],
flags: RecvFlags, flags: RecvFlags,
mut name: Option<&mut [u8]>, control: Option<&mut [u8]>,
mut control: Option<&mut [u8]>, ) -> Result<(usize, Option<RawAddr>, MsgFlags, usize)> {
) -> Result<(usize, usize, usize, MsgHdrFlags)> {
let current = current!(); let current = current!();
let data_length = data.iter().map(|s| s.len()).sum(); let data_length = data.iter().map(|s| s.len()).sum();
let mut ocall_alloc; let mut ocall_alloc;
@ -52,7 +34,7 @@ impl HostSocket {
} }
bufs bufs
}; };
let retval = self.do_recvmsg_untrusted_data(&mut u_data, flags, name, control)?; let retval = self.do_recvmsg_untrusted_data(&mut u_data, flags, control)?;
let mut remain = retval.0; let mut remain = retval.0;
for (i, buf) in data.iter_mut().enumerate() { for (i, buf) in data.iter_mut().enumerate() {
@ -71,16 +53,15 @@ impl HostSocket {
&self, &self,
data: &mut [UntrustedSlice], data: &mut [UntrustedSlice],
flags: RecvFlags, flags: RecvFlags,
mut name: Option<&mut [u8]>,
mut control: Option<&mut [u8]>, mut control: Option<&mut [u8]>,
) -> Result<(usize, usize, usize, MsgHdrFlags)> { ) -> Result<(usize, Option<RawAddr>, MsgFlags, usize)> {
// Prepare the arguments for OCall // Prepare the arguments for OCall
// Host socket fd
let host_fd = self.raw_host_fd() as i32; let host_fd = self.raw_host_fd() as i32;
// Name let mut addr = RawAddr::default();
let (msg_name, msg_namelen) = name.as_mut_ptr_and_len(); let mut msg_name = addr.as_mut_ptr();
let msg_name = msg_name as *mut c_void; let mut msg_namelen = addr.len();
let mut msg_namelen_recvd = 0_u32; let mut msg_namelen_recvd = 0_u32;
// Iovs // Iovs
let mut raw_iovs: Vec<libc::iovec> = data let mut raw_iovs: Vec<libc::iovec> = data
.iter() .iter()
@ -101,7 +82,7 @@ impl HostSocket {
let status = occlum_ocall_recvmsg( let status = occlum_ocall_recvmsg(
&mut retval as *mut isize, &mut retval as *mut isize,
host_fd, host_fd,
msg_name, msg_name as _,
msg_namelen as u32, msg_namelen as u32,
&mut msg_namelen_recvd as *mut u32, &mut msg_namelen_recvd as *mut u32,
msg_iov, msg_iov,
@ -119,7 +100,7 @@ impl HostSocket {
retval retval
}); });
let flags_recvd = MsgHdrFlags::from_bits(msg_flags_recvd).unwrap(); let flags_recvd = MsgFlags::from_bits(msg_flags_recvd).unwrap();
// Check values returned from outside the enclave // Check values returned from outside the enclave
let bytes_recvd = { let bytes_recvd = {
@ -133,21 +114,24 @@ impl HostSocket {
// For MSG_TRUNC recvmsg returns the real length of the packet or datagram, // For MSG_TRUNC recvmsg returns the real length of the packet or datagram,
// even when it was longer than the passed buffer. // even when it was longer than the passed buffer.
if flags.contains(RecvFlags::MSG_TRUNC) && retval > max_bytes_recvd { if flags.contains(RecvFlags::MSG_TRUNC) && retval > max_bytes_recvd {
assert!(flags_recvd.contains(MsgHdrFlags::MSG_TRUNC)); assert!(flags_recvd.contains(MsgFlags::MSG_TRUNC));
} else { } else {
assert!(retval <= max_bytes_recvd); assert!(retval <= max_bytes_recvd);
} }
retval retval
}; };
let msg_namelen_recvd = msg_namelen_recvd as usize; let msg_namelen_recvd = msg_namelen_recvd as usize;
let raw_addr = if msg_namelen_recvd == 0 {
None
} else {
addr.set_len(msg_namelen_recvd)?;
Some(addr)
};
assert!(msg_namelen_recvd <= msg_namelen); assert!(msg_namelen_recvd <= msg_namelen);
assert!(msg_controllen_recvd <= msg_controllen); assert!(msg_controllen_recvd <= msg_controllen);
Ok(( Ok((bytes_recvd, raw_addr, flags_recvd, msg_controllen_recvd))
bytes_recvd,
msg_namelen_recvd,
msg_controllen_recvd,
flags_recvd,
))
} }
} }

@ -5,22 +5,11 @@ impl HostSocket {
self.sendto(buf, flags, &None) self.sendto(buf, flags, &None)
} }
pub fn sendmsg<'a, 'b>(&self, msg: &'b MsgHdr<'a>, flags: SendFlags) -> Result<usize> { pub fn sendmsg(
let msg_iov = msg.get_iovs();
self.do_sendmsg(
msg_iov.as_slices(),
flags,
msg.get_name(),
msg.get_control(),
)
}
pub(super) fn do_sendmsg(
&self, &self,
data: &[&[u8]], data: &[&[u8]],
flags: SendFlags, flags: SendFlags,
name: Option<&[u8]>, addr: &Option<RawAddr>,
control: Option<&[u8]>, control: Option<&[u8]>,
) -> Result<usize> { ) -> Result<usize> {
let current = current!(); let current = current!();
@ -45,8 +34,8 @@ impl HostSocket {
bufs bufs
}; };
let retval = self.do_sendmsg_untrusted_data(&u_data, flags, name, control); let name = addr.as_ref().map(|raw_addr| raw_addr.as_slice());
retval self.do_sendmsg_untrusted_data(&u_data, flags, name, control)
} }
fn do_sendmsg_untrusted_data( fn do_sendmsg_untrusted_data(

@ -4,10 +4,11 @@ use std::io::{Read, Seek, SeekFrom, Write};
use atomic::{Atomic, Ordering}; use atomic::{Atomic, Ordering};
use super::*; use super::*;
use crate::fs::{AccessMode, File, HostFd, IoEvents, StatusFlags, STATUS_FLAGS_MASK};
use crate::fs::{ use crate::fs::{
occlum_ocall_ioctl, AccessMode, AtomicIoEvents, CreationFlags, File, FileRef, HostFd, IoEvents, GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, IoctlCmd, NonBuiltinIoctlCmd, SetNonBlocking,
IoctlCmd, StatusFlags, STATUS_FLAGS_MASK,
}; };
use crate::net::socket::sockopt::{GetSockOptRawCmd, SetSockOptRawCmd};
//TODO: refactor write syscall to allow zero length with non-zero buffer //TODO: refactor write syscall to allow zero length with non-zero buffer
impl File for HostSocket { impl File for HostSocket {
@ -34,20 +35,46 @@ impl File for HostSocket {
} }
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> { fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
let (bytes_recvd, _, _, _) = self.do_recvmsg(bufs, RecvFlags::empty(), None, None)?; let (bytes_recvd, _, _, _) = self.recvmsg(bufs, RecvFlags::empty(), None)?;
Ok(bytes_recvd) Ok(bytes_recvd)
} }
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> { fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
self.do_sendmsg(bufs, SendFlags::empty(), None, None) self.sendmsg(bufs, SendFlags::empty(), &None, None)
} }
fn seek(&self, pos: SeekFrom) -> Result<off_t> { fn seek(&self, pos: SeekFrom) -> Result<off_t> {
return_errno!(ESPIPE, "Socket does not support seek") return_errno!(ESPIPE, "Socket does not support seek")
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
self.ioctl_impl(cmd) match_ioctl_cmd_mut!(&mut *cmd, {
cmd: GetSockOptRawCmd => {
cmd.execute(self.raw_host_fd())?;
},
cmd: SetSockOptRawCmd => {
cmd.execute(self.raw_host_fd())?;
},
cmd: GetIfReqWithRawCmd => {
cmd.execute(self.raw_host_fd())?;
},
cmd: GetIfConf => {
cmd.execute(self.raw_host_fd())?;
},
cmd: GetReadBufLen => {
cmd.execute(self.raw_host_fd())?;
},
cmd: SetNonBlocking => {
cmd.execute(self.raw_host_fd())?;
},
cmd: NonBuiltinIoctlCmd => {
cmd.execute(self.raw_host_fd())?;
},
_ => {
return_errno!(EINVAL, "Not supported yet");
}
});
Ok(())
} }
fn access_mode(&self) -> Result<AccessMode> { fn access_mode(&self) -> Result<AccessMode> {

@ -1,354 +0,0 @@
/// Socket message and its flags.
use super::*;
/// C struct for a socket message with const pointers
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct msghdr {
pub msg_name: *const c_void,
pub msg_namelen: libc::socklen_t,
pub msg_iov: *const libc::iovec,
pub msg_iovlen: size_t,
pub msg_control: *const c_void,
pub msg_controllen: size_t,
pub msg_flags: c_int,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct mmsghdr {
pub msg_hdr: msghdr,
pub msg_len: c_uint,
}
/// C struct for a socket message with mutable pointers
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct msghdr_mut {
pub msg_name: *mut c_void,
pub msg_namelen: libc::socklen_t,
pub msg_iov: *mut libc::iovec,
pub msg_iovlen: size_t,
pub msg_control: *mut c_void,
pub msg_controllen: size_t,
pub msg_flags: c_int,
}
/// MsgHdr is a memory-safe, immutable wrapper of msghdr
pub struct MsgHdr<'a> {
name: Option<&'a [u8]>,
iovs: Iovs<'a>,
control: Option<&'a [u8]>,
flags: MsgHdrFlags,
c_self: &'a msghdr,
}
impl<'a> MsgHdr<'a> {
/// Wrap a unsafe msghdr into a safe MsgHdr
pub unsafe fn from_c(c_msg: &'a msghdr) -> Result<MsgHdr> {
// Convert c_msg's (*mut T, usize)-pair fields to Option<&mut [T]>
let name_opt_slice =
new_optional_slice(c_msg.msg_name as *const u8, c_msg.msg_namelen as usize);
let iovs_opt_slice = new_optional_slice(
c_msg.msg_iov as *const libc::iovec,
c_msg.msg_iovlen as usize,
);
let control_opt_slice = new_optional_slice(
c_msg.msg_control as *const u8,
c_msg.msg_controllen as usize,
);
let flags = MsgHdrFlags::from_bits_truncate(c_msg.msg_flags);
let iovs = {
let iovs_vec = match iovs_opt_slice {
Some(iovs_slice) => iovs_slice
.iter()
.flat_map(|iov| new_optional_slice(iov.iov_base as *const u8, iov.iov_len))
.collect(),
None => Vec::new(),
};
Iovs::new(iovs_vec)
};
Ok(Self {
name: name_opt_slice,
iovs: iovs,
control: control_opt_slice,
flags: flags,
c_self: c_msg,
})
}
pub fn get_iovs(&self) -> &Iovs {
&self.iovs
}
pub fn get_name(&self) -> Option<&[u8]> {
self.name
}
pub fn get_control(&self) -> Option<&[u8]> {
self.control
}
pub fn get_flags(&self) -> MsgHdrFlags {
self.flags
}
}
/// MsgHdrMut is a memory-safe, mutable wrapper of msghdr_mut
pub struct MsgHdrMut<'a> {
name: Option<&'a mut [u8]>,
iovs: IovsMut<'a>,
control: Option<&'a mut [u8]>,
flags: MsgHdrFlags,
c_self: &'a mut msghdr_mut,
}
// TODO: use macros to eliminate redundant code between MsgHdr and MsgHdrMut
impl<'a> MsgHdrMut<'a> {
/// Wrap a unsafe msghdr_mut into a safe MsgHdrMut
pub unsafe fn from_c(c_msg: &'a mut msghdr_mut) -> Result<MsgHdrMut> {
// Convert c_msg's (*mut T, usize)-pair fields to Option<&mut [T]>
let name_opt_slice =
new_optional_slice_mut(c_msg.msg_name as *mut u8, c_msg.msg_namelen as usize);
let iovs_opt_slice =
new_optional_slice_mut(c_msg.msg_iov as *mut libc::iovec, c_msg.msg_iovlen as usize);
let control_opt_slice =
new_optional_slice_mut(c_msg.msg_control as *mut u8, c_msg.msg_controllen as usize);
let flags = MsgHdrFlags::from_bits_truncate(c_msg.msg_flags);
let iovs = {
let iovs_vec = match iovs_opt_slice {
Some(iovs_slice) => iovs_slice
.iter()
.flat_map(|iov| new_optional_slice_mut(iov.iov_base as *mut u8, iov.iov_len))
.collect(),
None => Vec::new(),
};
IovsMut::new(iovs_vec)
};
Ok(Self {
name: name_opt_slice,
iovs: iovs,
control: control_opt_slice,
flags: flags,
c_self: c_msg,
})
}
/////////////////////////////////////////////////////////////////////////
// Immutable interfaces (same as MsgHdr)
/////////////////////////////////////////////////////////////////////////
pub fn get_iovs(&self) -> &IovsMut {
&self.iovs
}
pub fn get_name(&self) -> Option<&[u8]> {
self.name.as_ref().map(|name| &name[..])
}
pub fn get_control(&self) -> Option<&[u8]> {
self.control.as_ref().map(|control| &control[..])
}
pub fn get_flags(&self) -> MsgHdrFlags {
self.flags
}
/////////////////////////////////////////////////////////////////////////
// Mutable interfaces (unique to MsgHdrMut)
/////////////////////////////////////////////////////////////////////////
pub fn get_iovs_mut<'b>(&'b mut self) -> &'b mut IovsMut<'a> {
&mut self.iovs
}
pub fn get_name_mut(&mut self) -> Option<&mut [u8]> {
self.name.as_mut().map(|name| &mut name[..])
}
pub fn get_name_max_len(&self) -> usize {
self.name.as_ref().map(|name| name.len()).unwrap_or(0)
}
pub fn set_name_len(&mut self, new_name_len: usize) -> Result<()> {
if new_name_len > self.get_name_max_len() {
return_errno!(EINVAL, "new_name_len is too big");
}
self.c_self.msg_namelen = new_name_len as libc::socklen_t;
Ok(())
}
pub fn get_control_mut(&mut self) -> Option<&mut [u8]> {
self.control.as_mut().map(|control| &mut control[..])
}
pub fn get_control_max_len(&self) -> usize {
self.control
.as_ref()
.map(|control| control.len())
.unwrap_or(0)
}
pub fn set_control_len(&mut self, new_control_len: usize) -> Result<()> {
if new_control_len > self.get_control_max_len() {
return_errno!(EINVAL, "new_control_len is too big");
}
self.c_self.msg_controllen = new_control_len;
Ok(())
}
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.control.as_mut().map(|control| &mut control[..]),
)
}
pub fn set_flags(&mut self, flags: MsgHdrFlags) {
self.flags = flags;
self.c_self.msg_flags = flags.bits();
}
}
/// This struct is used to iterate through the control messages.
///
/// `cmsghdr` is a C struct for ancillary data object information of a unix socket.
pub struct CMessages<'a> {
buffer: &'a [u8],
current: Option<&'a libc::cmsghdr>,
}
impl<'a> Iterator for CMessages<'a> {
type Item = CmsgData<'a>;
fn next(&mut self) -> Option<Self::Item> {
let cmsg = unsafe {
let mut msg: libc::msghdr = core::mem::zeroed();
msg.msg_control = self.buffer.as_ptr() as *mut _;
msg.msg_controllen = self.buffer.len() as _;
let cmsg = if let Some(current) = self.current {
libc::CMSG_NXTHDR(&msg, current)
} else {
libc::CMSG_FIRSTHDR(&msg)
};
cmsg.as_ref()?
};
self.current = Some(cmsg);
CmsgData::try_from_cmsghdr(cmsg)
}
}
impl<'a> CMessages<'a> {
pub fn from_bytes(msg_control: &'a mut [u8]) -> Self {
Self {
buffer: msg_control,
current: None,
}
}
}
/// Control message data of variable type. The data resides next to `cmsghdr`.
pub enum CmsgData<'a> {
ScmRights(ScmRights<'a>),
ScmCredentials,
}
impl<'a> CmsgData<'a> {
/// Create an `CmsgData::ScmRights` variant.
///
/// # Safety
///
/// `data` must contain a valid control message and the control message must be type of
/// `SOL_SOCKET` and level of `SCM_RIGHTS`.
unsafe fn as_rights(data: &'a mut [u8]) -> Self {
let scm_rights = ScmRights { data };
CmsgData::ScmRights(scm_rights)
}
/// Create an `CmsgData::ScmCredentials` variant.
///
/// # Safety
///
/// `data` must contain a valid control message and the control message must be type of
/// `SOL_SOCKET` and level of `SCM_CREDENTIALS`.
unsafe fn as_credentials(_data: &'a [u8]) -> Self {
CmsgData::ScmCredentials
}
fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Option<Self> {
unsafe {
let cmsg_len_zero = libc::CMSG_LEN(0) as usize;
let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero;
let data = libc::CMSG_DATA(cmsg);
let data = core::slice::from_raw_parts_mut(data, data_len);
match (*cmsg).cmsg_level {
libc::SOL_SOCKET => match (*cmsg).cmsg_type {
libc::SCM_RIGHTS => Some(CmsgData::as_rights(data)),
libc::SCM_CREDENTIALS => Some(CmsgData::as_credentials(data)),
_ => None,
},
_ => None,
}
}
}
}
/// The data unit of this control message is file descriptor(s).
///
/// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_RIGHTS`.
pub struct ScmRights<'a> {
data: &'a mut [u8],
}
impl<'a> ScmRights<'a> {
/// Iterate and reassign each fd in data buffer, given a reassignment function.
pub fn iter_and_reassign_fds<F>(&mut self, reassign_fd_fn: F)
where
F: Fn(FileDesc) -> FileDesc,
{
for fd_bytes in self.data.chunks_exact_mut(core::mem::size_of::<FileDesc>()) {
let old_fd = FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap());
let reassigned_fd = reassign_fd_fn(old_fd);
fd_bytes.copy_from_slice(&reassigned_fd.to_ne_bytes());
}
}
pub fn iter_fds(&self) -> impl Iterator<Item = FileDesc> + '_ {
self.data
.chunks_exact(core::mem::size_of::<FileDesc>())
.map(|fd_bytes| FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap()))
}
}
unsafe fn new_optional_slice<'a, T>(slice_ptr: *const T, slice_size: usize) -> Option<&'a [T]> {
if !slice_ptr.is_null() {
let slice = core::slice::from_raw_parts::<T>(slice_ptr, slice_size);
Some(slice)
} else {
None
}
}
unsafe fn new_optional_slice_mut<'a, T>(
slice_ptr: *mut T,
slice_size: usize,
) -> Option<&'a mut [T]> {
if !slice_ptr.is_null() {
let slice = core::slice::from_raw_parts_mut::<T>(slice_ptr, slice_size);
Some(slice)
} else {
None
}
}

@ -1,28 +0,0 @@
use super::*;
bitflags! {
pub struct HowToShut: c_int {
const READ = 0;
const WRITE = 1;
const BOTH = 2;
}
}
impl HowToShut {
pub fn try_from_raw(how: c_int) -> Result<Self> {
match how {
0 => Ok(Self::READ),
1 => Ok(Self::WRITE),
2 => Ok(Self::BOTH),
_ => return_errno!(EINVAL, "invalid how"),
}
}
pub fn to_shut_read(&self) -> bool {
*self == Self::READ || *self == Self::BOTH
}
pub fn to_shut_write(&self) -> bool {
*self == Self::WRITE || *self == Self::BOTH
}
}

@ -1,29 +0,0 @@
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,156 +0,0 @@
use super::*;
use std::path::{Path, PathBuf};
use std::{cmp, mem, slice, str};
const MAX_PATH_LEN: usize = 108;
const SUN_FAMILY_LEN: usize = mem::size_of::<libc::sa_family_t>();
lazy_static! {
static ref SUN_PATH_OFFSET: usize = memoffset::offset_of!(libc::sockaddr_un, sun_path);
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Addr {
File(Option<usize>, UnixPath), // An optional inode number and path. Use inode if there is one.
Abstract(String),
}
impl Addr {
/// Caller should guarentee the sockaddr and addr_len are valid.
/// The pathname should end with a '\0' within the passed length.
/// The abstract name should both start and end with a '\0' within the passed length.
pub unsafe fn try_from_raw(
sockaddr: *const libc::sockaddr,
addr_len: libc::socklen_t,
) -> Result<Self> {
let addr_len = addr_len as usize;
// TODO: support autobind to validate when addr_len == SUN_FAMILY_LEN
if addr_len <= SUN_FAMILY_LEN {
return_errno!(EINVAL, "the address is too short.");
}
if addr_len > MAX_PATH_LEN + *SUN_PATH_OFFSET {
return_errno!(EINVAL, "the address is too long.");
}
if AddressFamily::try_from((*sockaddr).sa_family)? != AddressFamily::LOCAL {
return_errno!(EINVAL, "not a valid address for unix socket");
}
let sockaddr = sockaddr as *const libc::sockaddr_un;
let sun_path = (*sockaddr).sun_path;
if sun_path[0] == 0 {
let path_ptr = sun_path[1..(addr_len - *SUN_PATH_OFFSET)].as_ptr();
let path_slice =
slice::from_raw_parts(path_ptr as *const u8, addr_len - *SUN_PATH_OFFSET - 1);
Ok(Self::Abstract(
str::from_utf8(&path_slice).unwrap().to_string(),
))
} else {
let path_cstr = CStr::from_ptr(sun_path.as_ptr());
if path_cstr.to_bytes_with_nul().len() > MAX_PATH_LEN {
return_errno!(EINVAL, "no null in the address");
}
Ok(Self::File(None, UnixPath::new(path_cstr.to_str().unwrap())))
}
}
pub fn copy_to_slice(&self, dst: &mut [u8]) -> usize {
let (raw_addr, addr_len) = self.to_raw();
let src =
unsafe { std::slice::from_raw_parts(&raw_addr as *const _ as *const u8, addr_len) };
let copied = std::cmp::min(dst.len(), addr_len);
dst[..copied].copy_from_slice(&src[..copied]);
copied
}
pub fn raw_len(&self) -> usize {
/// The '/0' at the end of Self::File counts
self.path_str().len()
+ 1
+ *SUN_PATH_OFFSET
}
pub fn path_str(&self) -> &str {
match self {
Self::File(_, unix_path) => &unix_path.path_str(),
Self::Abstract(path) => &path,
}
}
fn to_raw(&self) -> (libc::sockaddr_un, usize) {
let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };
addr.sun_family = AddressFamily::LOCAL as libc::sa_family_t;
let addr_len = match self {
Self::File(_, unix_path) => {
let path_str = unix_path.path_str();
let buf_len = path_str.len();
/// addr is initialized to all zeros and try_from_raw guarentees
/// unix_path length is shorter than sun_path, so sun_path here
/// will always have a null terminator
addr.sun_path[..buf_len]
.copy_from_slice(unsafe { &*(path_str.as_bytes() as *const _ as *const [i8]) });
buf_len + *SUN_PATH_OFFSET + 1
}
Self::Abstract(path_str) => {
addr.sun_path[0] = 0;
let buf_len = path_str.len() + 1;
addr.sun_path[1..buf_len]
.copy_from_slice(unsafe { &*(path_str.as_bytes() as *const _ as *const [i8]) });
buf_len + *SUN_PATH_OFFSET
}
};
(addr, addr_len)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UnixPath {
inner: PathBuf,
/// Holds the cwd when a relative path is created
cwd: Option<String>,
}
impl UnixPath {
pub fn new(path: &str) -> Self {
let inner = PathBuf::from(path);
let is_absolute = inner.is_absolute();
Self {
inner: inner,
cwd: if is_absolute {
None
} else {
let thread = current!();
let fs = thread.fs().read().unwrap();
let cwd = fs.cwd().to_owned();
Some(cwd)
},
}
}
pub fn absolute(&self) -> String {
let path_str = self.path_str();
if self.inner.is_absolute() {
path_str.to_string()
} else {
let mut prefix = self.cwd.as_ref().unwrap().clone();
if prefix.ends_with("/") {
prefix.push_str(path_str);
} else {
prefix.push_str("/");
prefix.push_str(path_str);
}
prefix
}
}
pub fn path_str(&self) -> &str {
self.inner.to_str().unwrap()
}
}

@ -1,19 +1,16 @@
use self::addr::Addr;
use super::*; use super::*;
mod addr;
mod stream; mod stream;
pub use self::addr::Addr as UnixAddr;
pub use self::stream::Stream; pub use self::stream::Stream;
//TODO: rewrite this file when a new kind of uds is added //TODO: rewrite this file when a new kind of uds is added
pub fn unix_socket(socket_type: SocketType, flags: FileFlags, protocol: i32) -> Result<Stream> { pub fn unix_socket(socket_type: Type, flags: SocketFlags, protocol: i32) -> Result<Stream> {
if protocol != 0 && protocol != AddressFamily::LOCAL as i32 { if protocol != 0 && protocol != Domain::LOCAL as i32 {
return_errno!(EPROTONOSUPPORT, "protocol is not supported"); return_errno!(EPROTONOSUPPORT, "protocol is not supported");
} }
if socket_type == SocketType::STREAM { if socket_type == Type::STREAM {
Ok(Stream::new(flags)) Ok(Stream::new(flags))
} else { } else {
return_errno!(ESOCKTNOSUPPORT, "only stream type is supported"); return_errno!(ESOCKTNOSUPPORT, "only stream type is supported");
@ -21,15 +18,15 @@ pub fn unix_socket(socket_type: SocketType, flags: FileFlags, protocol: i32) ->
} }
pub fn socketpair( pub fn socketpair(
socket_type: SocketType, socket_type: Type,
flags: FileFlags, flags: SocketFlags,
protocol: i32, protocol: i32,
) -> Result<(Stream, Stream)> { ) -> Result<(Stream, Stream)> {
if protocol != 0 && protocol != AddressFamily::LOCAL as i32 { if protocol != 0 && protocol != Domain::LOCAL as i32 {
return_errno!(EPROTONOSUPPORT, "protocol is not supported"); return_errno!(EPROTONOSUPPORT, "protocol is not supported");
} }
if socket_type == SocketType::STREAM { if socket_type == Type::STREAM {
Stream::socketpair(flags) Stream::socketpair(flags)
} else { } else {
return_errno!(ESOCKTNOSUPPORT, "only stream type is supported"); return_errno!(ESOCKTNOSUPPORT, "only stream type is supported");

@ -39,9 +39,9 @@ impl AddressSpace {
} }
} }
pub fn add_binder(&self, addr: &Addr) -> Result<()> { pub fn add_binder(&self, addr: &UnixAddr) -> Result<()> {
let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "can't find socket file"))?; let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "can't find socket file"))?;
let mut space = self.get_space(addr); let mut space = self.get_space(addr)?;
if space.contains_key(&key) { if space.contains_key(&key) {
return_errno!(EADDRINUSE, "the addr is already bound"); return_errno!(EADDRINUSE, "the addr is already bound");
} else { } else {
@ -52,13 +52,13 @@ impl AddressSpace {
pub(super) fn add_listener( pub(super) fn add_listener(
&self, &self,
addr: &Addr, addr: &UnixAddr,
capacity: usize, capacity: usize,
nonblocking: bool, nonblocking: bool,
notifier: Arc<RelayNotifier>, notifier: Arc<RelayNotifier>,
) -> Result<()> { ) -> Result<()> {
let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "the socket is not bound"))?; let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "the socket is not bound"))?;
let mut space = self.get_space(addr); let mut space = self.get_space(addr)?;
if let Some(option) = space.get(&key) { if let Some(option) = space.get(&key) {
if option.is_none() { if option.is_none() {
@ -75,9 +75,9 @@ impl AddressSpace {
} }
} }
pub fn resize_listener(&self, addr: &Addr, capacity: usize) -> Result<()> { pub fn resize_listener(&self, addr: &UnixAddr, capacity: usize) -> Result<()> {
let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "the socket is not bound"))?; let key = Self::get_key(addr).ok_or_else(|| errno!(EINVAL, "the socket is not bound"))?;
let mut space = self.get_space(addr); let mut space = self.get_space(addr)?;
if let Some(option) = space.get(&key) { if let Some(option) = space.get(&key) {
if let Some(listener) = option { if let Some(listener) = option {
@ -91,33 +91,33 @@ impl AddressSpace {
} }
} }
pub fn push_incoming(&self, addr: &Addr, sock: Endpoint) -> Result<()> { pub fn push_incoming(&self, addr: &UnixAddr, sock: Endpoint) -> Result<()> {
self.get_listener_ref(addr) self.get_listener_ref(addr)
.ok_or_else(|| errno!(ECONNREFUSED, "no one's listening on the remote address"))? .ok_or_else(|| errno!(ECONNREFUSED, "no one's listening on the remote address"))?
.push_incoming(sock) .push_incoming(sock)
} }
pub fn pop_incoming(&self, addr: &Addr) -> Result<Endpoint> { pub fn pop_incoming(&self, addr: &UnixAddr) -> Result<Endpoint> {
self.get_listener_ref(addr) self.get_listener_ref(addr)
.ok_or_else(|| errno!(EINVAL, "the socket is not listening"))? .ok_or_else(|| errno!(EINVAL, "the socket is not listening"))?
.pop_incoming() .pop_incoming()
.ok_or_else(|| errno!(EAGAIN, "No connection is incoming")) .ok_or_else(|| errno!(EAGAIN, "No connection is incoming"))
} }
pub fn get_listener_ref(&self, addr: &Addr) -> Option<Arc<Listener>> { pub fn get_listener_ref(&self, addr: &UnixAddr) -> Option<Arc<Listener>> {
let key = Self::get_key(addr); let key = Self::get_key(addr);
if let Some(key) = key { if let Some(key) = key {
let space = self.get_space(addr); let space = self.get_space(addr).unwrap();
space.get(&key).map(|x| x.clone()).flatten() space.get(&key).map(|x| x.clone()).flatten()
} else { } else {
None None
} }
} }
pub fn remove_addr(&self, addr: &Addr) { pub fn remove_addr(&self, addr: &UnixAddr) {
let key = Self::get_key(addr); let key = Self::get_key(addr);
if let Some(key) = key { if let Some(key) = key {
let mut space = self.get_space(addr); let mut space = self.get_space(addr).unwrap();
space.remove(&key); space.remove(&key);
} else { } else {
warn!("address space key not exit: {:?}", addr); warn!("address space key not exit: {:?}", addr);
@ -126,21 +126,22 @@ impl AddressSpace {
fn get_space( fn get_space(
&self, &self,
addr: &Addr, addr: &UnixAddr,
) -> SgxMutexGuard<'_, BTreeMap<AddressSpaceKey, Option<Arc<Listener>>>> { ) -> Result<SgxMutexGuard<'_, BTreeMap<AddressSpaceKey, Option<Arc<Listener>>>>> {
match addr { match addr {
Addr::File(_, _) => self.file.lock().unwrap(), UnixAddr::File(_, _) => Ok(self.file.lock().unwrap()),
Addr::Abstract(_) => self.abstr.lock().unwrap(), UnixAddr::Abstract(_) => Ok(self.abstr.lock().unwrap()),
UnixAddr::Unnamed => return_errno!(EINVAL, "can't get path name for unnamed socket"),
} }
} }
fn get_key(addr: &Addr) -> Option<AddressSpaceKey> { fn get_key(addr: &UnixAddr) -> Option<AddressSpaceKey> {
trace!("addr = {:?}", addr); trace!("addr = {:?}", addr);
match addr { match addr {
Addr::File(inode_num, unix_path) if inode_num.is_some() => { UnixAddr::File(inode_num, unix_path) if inode_num.is_some() => {
Some(AddressSpaceKey::from_inode(inode_num.unwrap())) Some(AddressSpaceKey::from_inode(inode_num.unwrap()))
} }
Addr::File(_, unix_path) => { UnixAddr::File(_, unix_path) => {
let inode = { let inode = {
let file_path = unix_path.absolute(); let file_path = unix_path.absolute();
let current = current!(); let current = current!();
@ -153,7 +154,10 @@ impl AddressSpace {
None None
} }
} }
Addr::Abstract(path) => Some(AddressSpaceKey::from_path(addr.path_str().to_string())), UnixAddr::Abstract(path) => Some(AddressSpaceKey::from_path(
addr.path_str().unwrap().to_string(),
)),
UnixAddr::Unnamed => None,
} }
} }
} }

@ -39,7 +39,7 @@ pub fn end_pair(nonblocking: bool) -> Result<(Endpoint, Endpoint)> {
/// One end of the connected unix socket /// One end of the connected unix socket
pub struct Inner { pub struct Inner {
addr: RwLock<Option<Addr>>, addr: RwLock<Option<UnixAddr>>,
reader: Consumer<u8>, reader: Consumer<u8>,
writer: Producer<u8>, writer: Producer<u8>,
peer: Weak<Self>, peer: Weak<Self>,
@ -47,15 +47,15 @@ pub struct Inner {
} }
impl Inner { impl Inner {
pub fn addr(&self) -> Option<Addr> { pub fn addr(&self) -> Option<UnixAddr> {
self.addr.read().unwrap().clone() self.addr.read().unwrap().clone()
} }
pub fn set_addr(&self, addr: &Addr) { pub fn set_addr(&self, addr: &UnixAddr) {
*self.addr.write().unwrap() = Some(addr.clone()); *self.addr.write().unwrap() = Some(addr.clone());
} }
pub fn peer_addr(&self) -> Option<Addr> { pub fn peer_addr(&self) -> Option<UnixAddr> {
self.peer.upgrade().map(|end| end.addr().clone()).flatten() self.peer.upgrade().map(|end| end.addr().clone()).flatten()
} }
@ -90,16 +90,16 @@ impl Inner {
self.reader.items_to_consume() self.reader.items_to_consume()
} }
pub fn shutdown(&self, how: HowToShut) -> Result<()> { pub fn shutdown(&self, how: Shutdown) -> Result<()> {
if !self.is_connected() { if !self.is_connected() {
return_errno!(ENOTCONN, "The socket is not connected."); return_errno!(ENOTCONN, "The socket is not connected.");
} }
if how.to_shut_read() { if how.should_shut_read() {
self.reader.shutdown() self.reader.shutdown()
} }
if how.to_shut_write() { if how.should_shut_write() {
self.writer.shutdown() self.writer.shutdown()
} }

@ -1,10 +1,12 @@
use super::address_space::ADDRESS_SPACE; use super::address_space::ADDRESS_SPACE;
use super::stream::Status; use super::stream::Status;
use super::*; use super::*;
use fs::{AccessMode, File, FileRef, IoEvents, IoNotifier, IoctlCmd, StatusFlags}; use fs::{AccessMode, File, IoEvents, IoNotifier, StatusFlags};
use rcore_fs::vfs::{FileType, Metadata, Timespec}; use rcore_fs::vfs::{FileType, Metadata, Timespec};
use std::any::Any; use std::any::Any;
use crate::fs::{GetReadBufLen, IoctlCmd, SetNonBlocking};
impl File for Stream { impl File for Stream {
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
// The connected status will not be changed any more // The connected status will not be changed any more
@ -55,23 +57,23 @@ impl File for Stream {
} }
} }
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<i32> { fn ioctl(&self, cmd: &mut dyn IoctlCmd) -> Result<()> {
match cmd { match_ioctl_cmd_auto_error!(cmd, {
IoctlCmd::TCGETS(_) => return_errno!(ENOTTY, "not tty device"), cmd : GetReadBufLen => {
IoctlCmd::TCSETS(_) => return_errno!(ENOTTY, "not tty device"), match &*self.inner() {
IoctlCmd::FIONBIO(nonblocking) => {
self.set_nonblocking(**nonblocking != 0);
}
IoctlCmd::FIONREAD(arg) => match &*self.inner() {
Status::Connected(endpoint) => { Status::Connected(endpoint) => {
let bytes_to_read = endpoint.bytes_to_read().min(std::i32::MAX as usize) as i32; let bytes_to_read = endpoint.bytes_to_read().min(std::i32::MAX as usize) as i32;
**arg = bytes_to_read; cmd.set_output(bytes_to_read as _);
} }
_ => return_errno!(ENOTCONN, "unconnected socket"), _ => return_errno!(ENOTCONN, "unconnected socket"),
};
}, },
_ => return_errno!(EINVAL, "unknown ioctl cmd for unix socket"), cmd : SetNonBlocking => {
let nonblocking = cmd.input();
self.set_nonblocking(*nonblocking != 0);
} }
Ok(0) });
Ok(())
} }
fn access_mode(&self) -> Result<AccessMode> { fn access_mode(&self) -> Result<AccessMode> {

@ -5,7 +5,7 @@ use events::{Event, EventFilter, Notifier, Observer};
use fs::channel::Channel; use fs::channel::Channel;
use fs::IoEvents; use fs::IoEvents;
use fs::{CreationFlags, FileMode}; use fs::{CreationFlags, FileMode};
use net::socket::{CMessages, CmsgData, Iovs, MsgHdr, MsgHdrMut}; use net::socket::{CMessages, CmsgData};
use std::fmt; use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -23,17 +23,17 @@ pub struct Stream {
} }
impl Stream { impl Stream {
pub fn new(flags: FileFlags) -> Self { pub fn new(flags: SocketFlags) -> Self {
Self { Self {
inner: SgxMutex::new(Status::Idle(Info::new( inner: SgxMutex::new(Status::Idle(Info::new(
flags.contains(FileFlags::SOCK_NONBLOCK), flags.contains(SocketFlags::SOCK_NONBLOCK),
))), ))),
notifier: Arc::new(RelayNotifier::new()), notifier: Arc::new(RelayNotifier::new()),
} }
} }
pub fn socketpair(flags: FileFlags) -> Result<(Self, Self)> { pub fn socketpair(flags: SocketFlags) -> Result<(Self, Self)> {
let nonblocking = flags.contains(FileFlags::SOCK_NONBLOCK); let nonblocking = flags.contains(SocketFlags::SOCK_NONBLOCK);
let (end_a, end_b) = end_pair(nonblocking)?; let (end_a, end_b) = end_pair(nonblocking)?;
let notifier_a = Arc::new(RelayNotifier::new()); let notifier_a = Arc::new(RelayNotifier::new());
let notifier_b = Arc::new(RelayNotifier::new()); let notifier_b = Arc::new(RelayNotifier::new());
@ -53,15 +53,17 @@ impl Stream {
Ok((socket_a, socket_b)) Ok((socket_a, socket_b))
} }
pub fn addr(&self) -> Option<Addr> { pub fn addr(&self) -> UnixAddr {
match &*self.inner() { let addr_opt = match &*self.inner() {
Status::Idle(info) => info.addr().clone(), Status::Idle(info) => info.addr().clone(),
Status::Connected(endpoint) => endpoint.addr(), Status::Connected(endpoint) => endpoint.addr(),
Status::Listening(addr) => Some(addr).cloned(), Status::Listening(addr) => Some(addr).cloned(),
} };
addr_opt.unwrap_or(UnixAddr::Unnamed)
} }
pub fn peer_addr(&self) -> Result<Addr> { pub fn peer_addr(&self) -> Result<UnixAddr> {
if let Status::Connected(endpoint) = &*self.inner() { if let Status::Connected(endpoint) = &*self.inner() {
if let Some(addr) = endpoint.peer_addr() { if let Some(addr) = endpoint.peer_addr() {
return Ok(addr); return Ok(addr);
@ -70,8 +72,8 @@ impl Stream {
return_errno!(ENOTCONN, "the socket is not connected"); return_errno!(ENOTCONN, "the socket is not connected");
} }
pub fn bind(&self, addr: &mut Addr) -> Result<()> { pub fn bind(&self, addr: &mut UnixAddr) -> Result<()> {
if let Addr::File(inode_num, path) = addr { if let UnixAddr::File(inode_num, path) = addr {
// create the corresponding file in the fs and fill Addr with its inode // create the corresponding file in the fs and fill Addr with its inode
let corresponding_inode_num = { let corresponding_inode_num = {
let current = current!(); let current = current!();
@ -143,7 +145,7 @@ impl Stream {
/// The establishment of the connection is very fast and can be done immediately. /// The establishment of the connection is very fast and can be done immediately.
/// Therefore, the connect function in our implementation will never block. /// Therefore, the connect function in our implementation will never block.
pub fn connect(&self, addr: &Addr) -> Result<()> { pub fn connect(&self, addr: &UnixAddr) -> Result<()> {
debug!("connect to {:?}", addr); debug!("connect to {:?}", addr);
let mut inner = self.inner(); let mut inner = self.inner();
@ -168,7 +170,7 @@ impl Stream {
ADDRESS_SPACE ADDRESS_SPACE
.push_incoming(addr, end_incoming) .push_incoming(addr, end_incoming)
.map_err(|e| match e.errno() { .map_err(|e| match e.errno() {
Errno::EAGAIN => errno!(ECONNREFUSED, "the backlog is full"), EAGAIN => errno!(ECONNREFUSED, "the backlog is full"),
_ => e, _ => e,
})?; })?;
@ -187,12 +189,12 @@ impl Stream {
} }
} }
pub fn accept(&self, flags: FileFlags) -> Result<(Self, Option<Addr>)> { pub fn accept(&self, flags: SocketFlags) -> Result<(Self, Option<UnixAddr>)> {
let status = (*self.inner()).clone(); let status = (*self.inner()).clone();
match status { match status {
Status::Listening(addr) => { Status::Listening(addr) => {
let endpoint = ADDRESS_SPACE.pop_incoming(&addr)?; let endpoint = ADDRESS_SPACE.pop_incoming(&addr)?;
endpoint.set_nonblocking(flags.contains(FileFlags::SOCK_NONBLOCK)); endpoint.set_nonblocking(flags.contains(SocketFlags::SOCK_NONBLOCK));
endpoint.set_ancillary(Ancillary { endpoint.set_ancillary(Ancillary {
tid: current!().tid(), tid: current!().tid(),
}); });
@ -216,12 +218,12 @@ impl Stream {
} }
// TODO: handle flags // TODO: handle flags
pub fn sendto(&self, buf: &[u8], flags: SendFlags, addr: &Option<Addr>) -> Result<usize> { pub fn sendto(&self, buf: &[u8], flags: SendFlags, addr: &Option<UnixAddr>) -> Result<usize> {
self.write(buf) self.write(buf)
} }
// TODO: handle flags // TODO: handle flags
pub fn recvfrom(&self, buf: &mut [u8], flags: RecvFlags) -> Result<(usize, Option<Addr>)> { pub fn recvfrom(&self, buf: &mut [u8], flags: RecvFlags) -> Result<(usize, Option<UnixAddr>)> {
let data_len = self.read(buf)?; let data_len = self.read(buf)?;
let addr = self.peer_addr().ok(); let addr = self.peer_addr().ok();
@ -230,33 +232,39 @@ impl Stream {
Ok((data_len, addr)) Ok((data_len, addr))
} }
pub fn sendmsg(&self, msg_hdr: &MsgHdr, flags: SendFlags) -> Result<usize> { pub fn sendmsg(
&self,
bufs: &[&[u8]],
flags: SendFlags,
control: Option<&[u8]>,
) -> Result<usize> {
if !flags.is_empty() { if !flags.is_empty() {
warn!("unsupported flags: {:?}", flags); warn!("unsupported flags: {:?}", flags);
} }
let bufs = msg_hdr.get_iovs().as_slices(); let data_len = self.writev(bufs)?;
let mut data_len = self.writev(bufs)?; if let Some(msg_control) = control {
self.write(msg_control)?;
if let Some(msg_control) = msg_hdr.get_control() {
data_len += self.write(msg_control)?;
} }
Ok(data_len) Ok(data_len)
} }
pub fn recvmsg(&self, msg_hdr: &mut MsgHdrMut, flags: RecvFlags) -> Result<usize> { pub fn recvmsg(
&self,
bufs: &mut [&mut [u8]],
flags: RecvFlags,
control: Option<&mut [u8]>,
) -> Result<(usize, usize)> {
if !flags.is_empty() { if !flags.is_empty() {
warn!("unsupported flags: {:?}", flags); warn!("unsupported flags: {:?}", flags);
} }
let bufs = msg_hdr.get_iovs_mut().as_slices_mut(); let data_len = self.readv(bufs)?;
let mut data_len = self.readv(bufs)?;
// For stream socket, the msg_name is ignored. And other fields are not supported. // For stream socket, the msg_name is ignored. And other fields are not supported.
msg_hdr.set_name_len(0); let control_len = if let Some(msg_control) = control {
let control_len = self.read(msg_control)?;
if let Some(msg_control) = msg_hdr.get_control_mut() {
data_len += self.read(msg_control)?;
// For each control message that contains file descriptors (SOL_SOCKET and SCM_RIGHTS), // For each control message that contains file descriptors (SOL_SOCKET and SCM_RIGHTS),
// reassign each fd in the message in receive end. // reassign each fd in the message in receive end.
@ -268,7 +276,6 @@ impl Stream {
.unwrap() .unwrap()
.files() .files()
.lock() .lock()
.unwrap()
.get(send_fd) .get(send_fd)
.unwrap(); .unwrap();
current!().add_file(ipc_file.clone(), false) current!().add_file(ipc_file.clone(), false)
@ -276,12 +283,16 @@ impl Stream {
} }
// Unix credentials need not to be handled here // Unix credentials need not to be handled here
} }
} control_len
Ok(data_len) } else {
0
};
Ok((data_len, control_len))
} }
/// perform shutdown on the socket. /// perform shutdown on the socket.
pub fn shutdown(&self, how: HowToShut) -> Result<()> { pub fn shutdown(&self, how: Shutdown) -> Result<()> {
if let Status::Connected(ref end) = &*self.inner() { if let Status::Connected(ref end) = &*self.inner() {
end.shutdown(how) end.shutdown(how)
} else { } else {
@ -369,13 +380,13 @@ pub enum Status {
Idle(Info), Idle(Info),
// The listeners are stored in a global data structure indexed by the address. // The listeners are stored in a global data structure indexed by the address.
// The consitency of Status with that data structure should be carefully maintained. // The consitency of Status with that data structure should be carefully maintained.
Listening(Addr), Listening(UnixAddr),
Connected(Endpoint), Connected(Endpoint),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Info { pub struct Info {
addr: Option<Addr>, addr: Option<UnixAddr>,
nonblocking: bool, nonblocking: bool,
} }
@ -387,11 +398,11 @@ impl Info {
} }
} }
pub fn addr(&self) -> &Option<Addr> { pub fn addr(&self) -> &Option<UnixAddr> {
&self.addr &self.addr
} }
pub fn set_addr(&mut self, addr: &Addr) { pub fn set_addr(&mut self, addr: &UnixAddr) {
self.addr = Some(addr.clone()); self.addr = Some(addr.clone());
} }

@ -0,0 +1,134 @@
use crate::prelude::*;
use std::mem::{size_of, size_of_val, MaybeUninit};
/// A trait for all C version of C socket addresses.
///
/// There are four types that implement this trait:
/// * `libc::sockaddr_in`
/// * `(libc::sockaddr_in, usize)`
/// * `(libc::sockaddr_un, usize)`
/// * `(libc::sockaddr_storage, usize)`.
pub trait CSockAddr {
/// The network family of the address.
fn c_family(&self) -> libc::sa_family_t;
/// The address in bytes (excluding the family part).
fn c_addr(&self) -> &[u8];
/// Returns the address in `libc::sockaddr_storage` along with its length.
fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
let mut c_storage =
unsafe { MaybeUninit::<libc::sockaddr_storage>::uninit().assume_init() };
c_storage.ss_family = self.c_family();
let offset = size_of_val(&c_storage.ss_family);
let c_storage_len = offset + self.c_addr().len();
assert!(c_storage_len <= size_of::<libc::sockaddr_storage>());
let c_storage_remain = unsafe {
let ptr = (&mut c_storage as *mut _ as *mut u8).add(offset);
let len = self.c_addr().len();
std::slice::from_raw_parts_mut(ptr, len)
};
c_storage_remain.copy_from_slice(self.c_addr());
(c_storage, c_storage_len)
}
}
impl CSockAddr for libc::sockaddr_in {
fn c_family(&self) -> libc::sa_family_t {
libc::AF_INET as _
}
fn c_addr(&self) -> &[u8] {
// Safety. The slice is part of self.
unsafe {
let addr_ptr = (self as *const _ as *const u8).add(size_of_val(&self.sin_family));
std::slice::from_raw_parts(
addr_ptr,
size_of::<libc::sockaddr_in>() - size_of_val(&self.sin_family),
)
}
}
}
impl CSockAddr for (libc::sockaddr_in, usize) {
fn c_family(&self) -> libc::sa_family_t {
self.0.c_family()
}
fn c_addr(&self) -> &[u8] {
assert!(self.1 == size_of::<libc::sockaddr_in>());
self.0.c_addr()
}
}
impl CSockAddr for (libc::sockaddr_in6, usize) {
fn c_family(&self) -> libc::sa_family_t {
self.0.sin6_family
}
fn c_addr(&self) -> &[u8] {
assert!(self.1 == size_of::<libc::sockaddr_in6>());
unsafe {
let addr_ptr = (&self.0 as *const _ as *const u8).add(size_of_val(&self.c_family()));
std::slice::from_raw_parts(
addr_ptr,
size_of::<libc::sockaddr_in6>() - size_of_val(&self.c_family()),
)
}
}
}
impl CSockAddr for (libc::sockaddr_un, usize) {
fn c_family(&self) -> libc::sa_family_t {
libc::AF_UNIX as _
}
fn c_addr(&self) -> &[u8] {
assert!(
size_of::<libc::sa_family_t>() <= self.1 && self.1 <= size_of::<libc::sockaddr_un>()
);
// Safety. The slice is part of self.
unsafe {
let addr_ptr = (&self.0 as *const _ as *const u8).add(size_of_val(&self.0.sun_family));
std::slice::from_raw_parts(addr_ptr, self.1 - size_of_val(&self.0.sun_family))
}
}
}
impl CSockAddr for (libc::sockaddr_nl, usize) {
fn c_family(&self) -> libc::sa_family_t {
libc::AF_NETLINK as _
}
fn c_addr(&self) -> &[u8] {
assert!(self.1 == size_of::<libc::sockaddr_nl>());
unsafe {
let addr_ptr = (&self.0 as *const _ as *const u8).add(size_of_val(&self.c_family()));
std::slice::from_raw_parts(
addr_ptr,
size_of::<libc::sockaddr_nl>() - size_of_val(&self.c_family()),
)
}
}
}
impl CSockAddr for (libc::sockaddr_storage, usize) {
fn c_family(&self) -> libc::sa_family_t {
self.0.ss_family
}
fn c_addr(&self) -> &[u8] {
assert!(
size_of::<libc::sa_family_t>() <= self.1
&& self.1 <= size_of::<libc::sockaddr_storage>()
);
// Safety. The slice is part of self.
unsafe {
let addr_ptr = (&self.0 as *const _ as *const u8).add(size_of_val(&self.0.ss_family));
std::slice::from_raw_parts(addr_ptr, self.1 - size_of_val(&self.0.ss_family))
}
}
}

@ -0,0 +1,137 @@
use std::any::Any;
use std::fmt::{self, Debug};
use super::{Addr, CSockAddr, Domain, RawAddr};
use crate::prelude::*;
/// An IPv4 socket address, consisting of an IPv4 address and a port.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Ipv4SocketAddr {
ip: Ipv4Addr,
port: u16,
}
impl Addr for Ipv4SocketAddr {
fn domain() -> Domain {
Domain::INET
}
fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Result<Self> {
if c_addr_len > std::mem::size_of::<libc::sockaddr_storage>() {
return_errno!(EINVAL, "address length is too large");
}
// The c_addr_len is certainly not smaller than the length of IN_ADDR_ANY.
// https://en.wikipedia.org/wiki/IPv4
if c_addr_len < std::mem::size_of::<libc::sockaddr_in>() {
return_errno!(EINVAL, "address length is too small");
}
// Safe to convert from sockaddr_storage to sockaddr_in
let c_addr = unsafe { std::mem::transmute(c_addr) };
Self::from_c(c_addr)
}
fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
let c_in_addr = self.to_c();
c_in_addr.to_c_storage()
}
fn as_any(&self) -> &dyn Any {
self
}
fn is_default(&self) -> bool {
let inaddr_any = Self::default();
*self == inaddr_any
}
}
impl Ipv4SocketAddr {
pub fn new(ip: Ipv4Addr, port: u16) -> Self {
Self { ip, port }
}
pub fn from_c(c_addr: &libc::sockaddr_in) -> Result<Self> {
if c_addr.sin_family != libc::AF_INET as libc::sa_family_t {
return_errno!(EINVAL, "an ipv4 address is expected");
}
Ok(Self {
port: u16::from_be(c_addr.sin_port),
ip: Ipv4Addr::from_c(&c_addr.sin_addr),
})
}
pub fn to_c(&self) -> libc::sockaddr_in {
libc::sockaddr_in {
sin_family: libc::AF_INET as _,
sin_port: self.port.to_be(),
sin_addr: self.ip.to_c(),
sin_zero: [0; 8],
}
}
pub fn to_raw(&self) -> RawAddr {
let (storage, len) = self.to_c_storage();
RawAddr::from_c_storage(&storage, len)
}
pub fn ip(&self) -> &Ipv4Addr {
&self.ip
}
pub fn port(&self) -> u16 {
self.port
}
pub fn set_ip(&mut self, new_ip: Ipv4Addr) {
self.ip = new_ip;
}
pub fn set_port(&mut self, new_port: u16) {
self.port = new_port;
}
}
impl Default for Ipv4SocketAddr {
fn default() -> Self {
let addr = Ipv4Addr::new(0, 0, 0, 0);
Self::new(addr, 0)
}
}
/// An Ipv4 address.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Ipv4Addr([u8; 4] /* big endian */);
impl Ipv4Addr {
/// Creates a new IPv4 address of `a.b.c.d`.
pub fn new(a: u8, b: u8, c: u8, d: u8) -> Self {
Self([a, b, c, d])
}
/// Creates a new IPv4 address from its C counterpart.
pub fn from_c(c_addr: &libc::in_addr) -> Self {
Self(c_addr.s_addr.to_ne_bytes())
}
/// Return the C counterpart.
pub fn to_c(&self) -> libc::in_addr {
libc::in_addr {
s_addr: u32::from_ne_bytes(self.0),
}
}
/// Return the four digits that make up the address.
///
/// Assuming the address is `a.b.c.d`, the returned value would be `[a, b, c, d]`.
pub fn octets(&self) -> &[u8; 4] {
&self.0
}
}
impl fmt::Debug for Ipv4Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let [a, b, c, d] = *self.octets();
write!(f, "Ipv4Addr ({}.{}.{}.{})", &a, &b, &c, &d)
}
}

@ -0,0 +1,115 @@
use std::any::Any;
use std::fmt::Debug;
use super::RawAddr;
use super::{Addr, CSockAddr, Domain};
use crate::prelude::*;
use libc::in6_addr;
use libc::sockaddr_in6;
pub use std::net::Ipv6Addr;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Ipv6SocketAddr {
ip: Ipv6Addr,
port: u16,
flowinfo: u32,
scope_id: u32,
}
impl Addr for Ipv6SocketAddr {
fn domain() -> Domain {
Domain::INET6
}
fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Result<Self> {
if c_addr_len > std::mem::size_of::<libc::sockaddr_storage>() {
return_errno!(EINVAL, "address length is too large");
}
if c_addr_len < std::mem::size_of::<sockaddr_in6>() {
return_errno!(EINVAL, "address length is too small");
}
// Safe to convert from sockaddr_storage to sockaddr_in
let c_addr: &sockaddr_in6 = unsafe { std::mem::transmute(c_addr) };
Self::from_c(c_addr)
}
fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
let c_addr = self.to_c();
(c_addr, std::mem::size_of::<libc::sockaddr_in6>()).to_c_storage()
}
fn as_any(&self) -> &dyn Any {
self
}
fn is_default(&self) -> bool {
let in6addr_any_init = Self::default();
*self == in6addr_any_init
}
}
impl Ipv6SocketAddr {
pub fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> Self {
Self {
ip,
port,
flowinfo,
scope_id,
}
}
pub fn from_c(c_addr: &libc::sockaddr_in6) -> Result<Self> {
if c_addr.sin6_family != libc::AF_INET6 as libc::sa_family_t {
return_errno!(EINVAL, "an ipv6 address is expected");
}
Ok(Self {
port: u16::from_be(c_addr.sin6_port),
ip: Ipv6Addr::from(c_addr.sin6_addr.s6_addr),
flowinfo: u32::from_be(c_addr.sin6_flowinfo),
scope_id: u32::from_be(c_addr.sin6_scope_id),
})
}
pub fn to_c(&self) -> libc::sockaddr_in6 {
let in6_addr = in6_addr {
s6_addr: self.ip.octets(),
};
libc::sockaddr_in6 {
sin6_family: libc::AF_INET6 as _,
sin6_port: self.port.to_be(),
sin6_addr: in6_addr,
sin6_flowinfo: self.flowinfo.to_be(),
sin6_scope_id: self.flowinfo.to_be(),
}
}
pub fn to_raw(&self) -> RawAddr {
let (storage, len) = self.to_c_storage();
RawAddr::from_c_storage(&storage, len)
}
pub fn ip(&self) -> &Ipv6Addr {
&self.ip
}
pub fn port(&self) -> u16 {
self.port
}
pub fn set_ip(&mut self, new_ip: Ipv6Addr) {
self.ip = new_ip;
}
pub fn set_port(&mut self, new_port: u16) {
self.port = new_port;
}
}
impl Default for Ipv6SocketAddr {
fn default() -> Self {
let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
Self::new(addr, 0, 0, 0)
}
}

@ -0,0 +1,87 @@
use std::any::Any;
use std::fmt::Debug;
use crate::net::Domain;
use crate::prelude::*;
mod c_sock_addr;
mod ipv4;
mod ipv6;
mod raw_addr;
mod unix_addr;
/// A trait for network addresses.
pub trait Addr: Clone + Debug + Default + PartialEq + Send + Sync {
/// Return the domain that the address belongs to.
fn domain() -> Domain
where
Self: Sized;
/// Construct a new address from C's sockaddr_storage.
///
/// The length argument specify how much bytes in the given sockaddr_storage are to be
/// interpreted as parts of the address.
fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Result<Self>
where
Self: Sized;
/// Converts the address to C's sockaddr_storage.
///
/// The actual length used in sockaddr_storage is also returned.
fn to_c_storage(&self) -> (libc::sockaddr_storage, usize);
fn as_any(&self) -> &dyn Any;
fn is_default(&self) -> bool;
}
pub use self::c_sock_addr::CSockAddr;
pub use self::ipv4::{Ipv4Addr, Ipv4SocketAddr};
pub use self::ipv6::{Ipv6Addr, Ipv6SocketAddr};
pub use self::raw_addr::RawAddr;
pub use self::unix_addr::UnixAddr;
#[cfg(test)]
mod tests {
use std::mem::size_of;
use super::*;
#[test]
fn ipv4_to_and_from_c() {
let addr = [127u8, 0, 0, 1];
let port = 8888u16;
let c_addr = libc::sockaddr_in {
sin_family: libc::AF_INET as _,
sin_port: port.to_be(),
sin_addr: libc::in_addr {
s_addr: u32::from_be_bytes(addr).to_be(),
},
sin_zero: [0u8; 8],
};
let addr = {
let addr = Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]);
Ipv4SocketAddr::new(addr, port)
};
check_to_and_from_c(&c_addr, &addr);
}
fn check_to_and_from_c<T: CSockAddr, U: Addr>(c_addr: &T, addr: &U) {
let c_addr_storage = c_addr.to_c_storage();
// To C
assert!(are_sock_addrs_equal(c_addr, &addr.to_c_storage()));
assert!(are_sock_addrs_equal(&c_addr_storage, &addr.to_c_storage()));
// From C
let (c_addr_storage, c_addr_len) = c_addr_storage;
assert!(&U::from_c_storage(&c_addr_storage, c_addr_len).unwrap() == addr);
}
fn are_sock_addrs_equal<T: CSockAddr, U: CSockAddr>(one: &T, other: &U) -> bool {
one.c_family() == other.c_family() && one.c_addr() == other.c_addr()
}
}

@ -2,25 +2,33 @@ use super::*;
use std::*; use std::*;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SockAddr { pub struct RawAddr {
storage: libc::sockaddr_storage, storage: libc::sockaddr_storage,
len: usize, len: usize,
} }
// TODO: add more fields // TODO: add more fields
impl fmt::Debug for SockAddr { impl fmt::Debug for RawAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SockAddr") f.debug_struct("RawAddr")
.field( .field("family", &Domain::try_from(self.storage.ss_family).unwrap())
"family",
&AddressFamily::try_from(self.storage.ss_family).unwrap(),
)
.field("len", &self.len) .field("len", &self.len)
.finish() .finish()
} }
} }
impl SockAddr { impl RawAddr {
pub fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Self {
Self {
storage: *c_addr,
len: c_addr_len,
}
}
pub fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
(self.storage, self.len)
}
// Caller should guarentee the sockaddr and addr_len are valid // Caller should guarentee the sockaddr and addr_len are valid
pub unsafe fn try_from_raw( pub unsafe fn try_from_raw(
sockaddr: *const libc::sockaddr, sockaddr: *const libc::sockaddr,
@ -34,13 +42,13 @@ impl SockAddr {
return_errno!(EINVAL, "the address is too long."); return_errno!(EINVAL, "the address is too long.");
} }
match AddressFamily::try_from((*sockaddr).sa_family)? { match Domain::try_from((*sockaddr).sa_family)? {
AddressFamily::INET => { Domain::INET => {
if addr_len < std::mem::size_of::<libc::sockaddr_in>() as u32 { if addr_len < std::mem::size_of::<libc::sockaddr_in>() as u32 {
return_errno!(EINVAL, "short ipv4 address."); return_errno!(EINVAL, "short ipv4 address.");
} }
} }
AddressFamily::INET6 => { Domain::INET6 => {
let ipv6_addr_len = std::mem::size_of::<libc::sockaddr_in6>() as u32; let ipv6_addr_len = std::mem::size_of::<libc::sockaddr_in6>() as u32;
// Omit sin6_scope_id when it is not fully provided // Omit sin6_scope_id when it is not fully provided
// 4 represents the size of sin6_scope_id which is not a must // 4 represents the size of sin6_scope_id which is not a must
@ -110,7 +118,7 @@ impl SockAddr {
} }
} }
impl Default for SockAddr { impl Default for RawAddr {
fn default() -> Self { fn default() -> Self {
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
Self { Self {

@ -0,0 +1,212 @@
use crate::net::socket::CSockAddr;
use super::*;
use sgx_trts::c_str::CStr;
use std::path::{Path, PathBuf};
use std::{cmp, mem, slice, str};
const MAX_PATH_LEN: usize = 108;
const SUN_FAMILY_LEN: usize = mem::size_of::<libc::sa_family_t>();
lazy_static! {
static ref SUN_PATH_OFFSET: usize = memoffset::offset_of!(libc::sockaddr_un, sun_path);
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnixAddr {
Unnamed,
File(Option<usize>, UnixPath), // An optional inode number and path. Use inode if there is one.
Abstract(String),
}
impl UnixAddr {
/// Construct a unix address from its C counterpart.
///
/// The argument `c_len` specifies the length of the valid part in the given
/// C address.
pub fn from_c(c_addr: &libc::sockaddr_un, c_len: usize) -> Result<Self> {
// Sanity checks
if c_addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
return_errno!(EINVAL, "an unix address is expected");
}
if c_len < std::mem::size_of::<libc::sa_family_t>() {
return_errno!(EINVAL, "the length of the address is too small");
} else if c_len > std::mem::size_of::<libc::sockaddr_un>() {
return_errno!(EINVAL, "the length of the address is too large");
}
if c_len == std::mem::size_of::<libc::sa_family_t>() {
return Ok(Self::Unnamed);
}
let path_len = c_len - std::mem::size_of::<libc::sa_family_t>();
debug_assert!(path_len > 1);
if path_len == 1 {
// Both pathname and abstract addresses require a path_len greater than 1.
return_errno!(EINVAL, "the pathname must not be empty");
}
// A pathname address
if c_addr.sun_path[0] != 0 {
// More sanity check
let last_char = c_addr.sun_path[path_len - 1];
if last_char != 0 {
return_errno!(EINVAL, "the pathname is not null-terminated");
}
let pathname = {
// Safety. Converting from &[c_char] to &[i8] is harmless.
let path_slice: &[i8] = unsafe {
let char_slice = &c_addr.sun_path[..(path_len - 1)];
std::mem::transmute(char_slice)
};
let path_cstr = unsafe { CStr::from_ptr(path_slice.as_ptr()) };
if path_cstr.to_bytes_with_nul().len() > MAX_PATH_LEN {
return_errno!(EINVAL, "no null in the address");
}
path_cstr
.to_str()
.map_err(|_| errno!(EINVAL, "path is not UTF8"))?
.to_string()
};
Ok(Self::File(None, UnixPath::new(&pathname)))
}
// An abstract address
else {
// Safety. Converting from &[c_char] to &[u8] is harmless.
let u8_slice: &[u8] = unsafe {
let char_slice = &c_addr.sun_path[1..(path_len)];
std::mem::transmute(char_slice)
};
Ok(Self::Abstract(
str::from_utf8(u8_slice).unwrap().to_string(),
))
}
}
pub fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Result<Self> {
if (c_addr_len) > std::mem::size_of::<libc::sockaddr_storage>() {
return_errno!(EINVAL, "address length is too large");
}
// Safety. Convert from sockaddr_storage to sockaddr_un is harmless.
let c_addr = unsafe { std::mem::transmute(c_addr) };
unsafe { Self::from_c(c_addr, c_addr_len) }
}
pub fn copy_to_slice(&self, dst: &mut [u8]) -> usize {
let (raw_addr, addr_len) = self.to_c();
let src =
unsafe { std::slice::from_raw_parts(&raw_addr as *const _ as *const u8, addr_len) };
let copied = std::cmp::min(dst.len(), addr_len);
dst[..copied].copy_from_slice(&src[..copied]);
copied
}
pub fn raw_len(&self) -> usize {
/// The '/0' at the end of Self::File counts
match self.path_str() {
Ok(str) => str.len() + 1 + *SUN_PATH_OFFSET,
Err(_) => 0,
}
}
pub fn path_str(&self) -> Result<&str> {
match self {
Self::File(_, unix_path) => Ok(&unix_path.path_str()),
Self::Abstract(path) => Ok(&path),
Self::Unnamed => return_errno!(EINVAL, "can't get path name for unnamed socket"),
}
}
pub fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
let c_un_addr = self.to_c();
c_un_addr.to_c_storage()
}
pub fn to_raw(&self) -> RawAddr {
let (storage, addr_len) = self.to_c_storage();
RawAddr::from_c_storage(&storage, addr_len)
}
fn to_c(&self) -> (libc::sockaddr_un, usize) {
const FAMILY_LEN: usize = std::mem::size_of::<libc::sa_family_t>();
let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };
addr.sun_family = Domain::LOCAL as libc::sa_family_t;
let addr_len = match self {
Self::Unnamed => FAMILY_LEN,
Self::File(_, unix_path) => {
let path_str = unix_path.path_str();
let buf_len = path_str.len();
/// addr is initialized to all zeros and try_from_raw guarentees
/// unix_path length is shorter than sun_path, so sun_path here
/// will always have a null terminator
addr.sun_path[..buf_len]
.copy_from_slice(unsafe { &*(path_str.as_bytes() as *const _ as *const [i8]) });
buf_len + *SUN_PATH_OFFSET + 1
}
Self::Abstract(path_str) => {
addr.sun_path[0] = 0;
let buf_len = path_str.len() + 1;
addr.sun_path[1..buf_len]
.copy_from_slice(unsafe { &*(path_str.as_bytes() as *const _ as *const [i8]) });
buf_len + *SUN_PATH_OFFSET
}
};
(addr, addr_len)
}
}
impl Default for UnixAddr {
fn default() -> Self {
UnixAddr::Unnamed
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UnixPath {
inner: PathBuf,
/// Holds the cwd when a relative path is created
cwd: Option<String>,
}
impl UnixPath {
pub fn new(path: &str) -> Self {
let inner = PathBuf::from(path);
let is_absolute = inner.is_absolute();
Self {
inner: inner,
cwd: if is_absolute {
None
} else {
let thread = current!();
let fs = thread.fs().read().unwrap();
let cwd = fs.cwd().to_owned();
Some(cwd)
},
}
}
pub fn absolute(&self) -> String {
let path_str = self.path_str();
if self.inner.is_absolute() {
path_str.to_string()
} else {
let mut prefix = self.cwd.as_ref().unwrap().clone();
if prefix.ends_with("/") {
prefix.push_str(path_str);
} else {
prefix.push_str("/");
prefix.push_str(path_str);
}
prefix
}
}
pub fn path_str(&self) -> &str {
self.inner.to_str().unwrap()
}
}

@ -0,0 +1,113 @@
use std::mem::{self, MaybeUninit};
use crate::net::socket::Domain;
use crate::prelude::*;
use super::{Addr, CSockAddr, Ipv4Addr, Ipv4SocketAddr, Ipv6SocketAddr, RawAddr, UnixAddr};
use num_enum::IntoPrimitive;
use std::path::Path;
#[derive(Clone, Debug)]
pub enum AnyAddr {
Ipv4(Ipv4SocketAddr),
Ipv6(Ipv6SocketAddr),
Unix(UnixAddr),
Raw(RawAddr),
Unspec,
}
impl AnyAddr {
pub fn from_c_storage(c_addr: &libc::sockaddr_storage, c_addr_len: usize) -> Result<Self> {
let any_addr = match c_addr.ss_family as _ {
libc::AF_INET => {
let ipv4_addr = Ipv4SocketAddr::from_c_storage(c_addr, c_addr_len)?;
Self::Ipv4(ipv4_addr)
}
libc::AF_INET6 => {
let ipv6_addr = Ipv6SocketAddr::from_c_storage(c_addr, c_addr_len)?;
Self::Ipv6(ipv6_addr)
}
libc::AF_UNSPEC => Self::Unspec,
libc::AF_UNIX | libc::AF_LOCAL => {
let unix_addr = UnixAddr::from_c_storage(c_addr, c_addr_len)?;
Self::Unix(unix_addr)
}
_ => {
let raw_addr = RawAddr::from_c_storage(c_addr, c_addr_len);
Self::Raw(raw_addr)
}
};
Ok(any_addr)
}
pub fn to_c_storage(&self) -> (libc::sockaddr_storage, usize) {
match self {
Self::Ipv4(ipv4_addr) => ipv4_addr.to_c_storage(),
Self::Ipv6(ipv6_addr) => ipv6_addr.to_c_storage(),
Self::Unix(unix_addr) => unix_addr.to_c_storage(),
Self::Raw(raw_addr) => raw_addr.to_c_storage(),
Self::Unspec => {
let mut sockaddr_storage =
unsafe { MaybeUninit::<libc::sockaddr_storage>::uninit().assume_init() };
sockaddr_storage.ss_family = libc::AF_UNSPEC as _;
(sockaddr_storage, mem::size_of::<libc::sa_family_t>())
}
}
}
pub fn to_raw(&self) -> RawAddr {
match self {
Self::Ipv4(ipv4_addr) => ipv4_addr.to_raw(),
Self::Ipv6(ipv6_addr) => ipv6_addr.to_raw(),
Self::Unix(unix_addr) => unix_addr.to_raw(),
Self::Raw(raw_addr) => *raw_addr,
Self::Unspec => {
let mut sockaddr_storage =
unsafe { MaybeUninit::<libc::sockaddr_storage>::uninit().assume_init() };
sockaddr_storage.ss_family = libc::AF_UNSPEC as _;
RawAddr::from_c_storage(&sockaddr_storage, mem::size_of::<libc::sa_family_t>())
}
}
}
pub fn to_unix(&self) -> Result<&UnixAddr> {
match self {
Self::Unix(unix_addr) => Ok(unix_addr),
_ => return_errno!(EAFNOSUPPORT, "not unix address"),
}
}
pub fn as_ipv4(&self) -> Option<&Ipv4SocketAddr> {
match self {
Self::Ipv4(ipv4_addr) => Some(ipv4_addr),
_ => None,
}
}
pub fn to_ipv4(&self) -> Result<&Ipv4SocketAddr> {
match self {
Self::Ipv4(ipv4_addr) => Ok(ipv4_addr),
_ => return_errno!(EAFNOSUPPORT, "not ipv4 address"),
}
}
pub fn to_ipv6(&self) -> Result<&Ipv6SocketAddr> {
match self {
Self::Ipv6(ipv6_addr) => Ok(ipv6_addr),
_ => return_errno!(EAFNOSUPPORT, "not ipv6 address"),
}
}
pub fn is_unspec(&self) -> bool {
match self {
Self::Unspec => true,
_ => false,
}
}
pub fn as_slice(&self) -> &[u8] {
let (storage, len) = self.to_c_storage();
let addr = &storage as *const _ as *const _;
unsafe { std::slice::from_raw_parts(addr as *const u8, len) }
}
}

@ -1,10 +1,11 @@
use super::*; use super::*;
use num_enum::{IntoPrimitive, TryFromPrimitive};
// The protocol family generally is the same as the address family // The protocol family generally is the same as the address family
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
#[repr(u16)] #[repr(u16)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum AddressFamily { pub enum Domain {
UNSPEC = 0, UNSPEC = 0,
LOCAL = 1, LOCAL = 1,
/* Hide the families with the same number /* Hide the families with the same number
@ -60,7 +61,7 @@ pub enum AddressFamily {
MAX = 45, MAX = 45,
} }
impl AddressFamily { impl Domain {
pub fn try_from(af: u16) -> Result<Self> { pub fn try_from(af: u16) -> Result<Self> {
if af >= Self::MAX as u16 { if af >= Self::MAX as u16 {
return_errno!(EINVAL, "Unknown address family"); return_errno!(EINVAL, "Unknown address family");

@ -0,0 +1,39 @@
use bitflags::bitflags;
// Flags to use when sending data through a socket
bitflags! {
pub struct SendFlags: i32 {
const MSG_OOB = 0x01; // Sends out-of-band data on sockets
const MSG_DONTROUTE = 0x04; // Don't use a gateway to send out the packet
const MSG_DONTWAIT = 0x40; // Nonblocking io
const MSG_EOR = 0x80; // End of record
const MSG_CONFIRM = 0x0800; // Confirm path validity
const MSG_NOSIGNAL = 0x4000; // Do not generate SIGPIPE
const MSG_MORE = 0x8000; // Sender will send more
}
}
// Flags to use when receiving data through a socket
bitflags! {
pub struct RecvFlags: i32 {
const MSG_OOB = 0x01; // Recv out-of-band data
const MSG_PEEK = 0x02; // Return data without removing that
const MSG_TRUNC = 0x20; // Return the real length of the packet or datagram
const MSG_DONTWAIT = 0x40; // Nonblocking io
const MSG_WAITALL = 0x0100; // Wait for a full request
const MSG_ERRQUEUE = 0x2000; // Fetch message from error queue
// recvmsg only
const MSG_CMSG_CLOEXEC = 0x40000000; // Set close_on_exec for file descriptor received through SCM_RIGHTS
}
}
bitflags! {
pub struct MsgFlags: i32 {
const MSG_OOB = 0x01; // Expedited or out-of-band data was received
const MSG_CTRUNC = 0x08; // Some control data was discarded
const MSG_TRUNC = 0x20; // The trailing portion of a datagram was discarded
const MSG_EOR = 0x80; // End of record
const MSG_ERRQUEUE = 0x2000; // Fetch message from error queue
const MSG_NOTIFICATION = 0x8000; // Only applicable to SCTP socket
}
}

@ -0,0 +1,27 @@
use crate::prelude::*;
use crate::untrusted::{
SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSlice, UntrustedSliceAlloc,
};
use std;
mod addr;
mod any_addr;
mod domain;
mod flags;
mod iovs;
mod msg;
mod protocol;
mod shutdown;
mod r#type;
pub use self::addr::{
Addr, CSockAddr, Ipv4Addr, Ipv4SocketAddr, Ipv6SocketAddr, RawAddr, UnixAddr,
};
pub use self::any_addr::AnyAddr;
pub use self::domain::Domain;
pub use self::flags::{MsgFlags, RecvFlags, SendFlags};
pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec};
pub use self::msg::{CMessages, CmsgData};
pub use self::protocol::SocketProtocol;
pub use self::r#type::Type;
pub use self::shutdown::Shutdown;

@ -0,0 +1,115 @@
/// Socket message and its flags.
use super::*;
/// This struct is used to iterate through the control messages.
///
/// `cmsghdr` is a C struct for ancillary data object information of a unix socket.
pub struct CMessages<'a> {
buffer: &'a [u8],
current: Option<&'a libc::cmsghdr>,
}
impl<'a> Iterator for CMessages<'a> {
type Item = CmsgData<'a>;
fn next(&mut self) -> Option<Self::Item> {
let cmsg = unsafe {
let mut msg: libc::msghdr = core::mem::zeroed();
msg.msg_control = self.buffer.as_ptr() as *mut _;
msg.msg_controllen = self.buffer.len() as _;
let cmsg = if let Some(current) = self.current {
libc::CMSG_NXTHDR(&msg, current)
} else {
libc::CMSG_FIRSTHDR(&msg)
};
cmsg.as_ref()?
};
self.current = Some(cmsg);
CmsgData::try_from_cmsghdr(cmsg)
}
}
impl<'a> CMessages<'a> {
pub fn from_bytes(msg_control: &'a mut [u8]) -> Self {
Self {
buffer: msg_control,
current: None,
}
}
}
/// Control message data of variable type. The data resides next to `cmsghdr`.
pub enum CmsgData<'a> {
ScmRights(ScmRights<'a>),
ScmCredentials,
}
impl<'a> CmsgData<'a> {
/// Create an `CmsgData::ScmRights` variant.
///
/// # Safety
///
/// `data` must contain a valid control message and the control message must be type of
/// `SOL_SOCKET` and level of `SCM_RIGHTS`.
unsafe fn as_rights(data: &'a mut [u8]) -> Self {
let scm_rights = ScmRights { data };
CmsgData::ScmRights(scm_rights)
}
/// Create an `CmsgData::ScmCredentials` variant.
///
/// # Safety
///
/// `data` must contain a valid control message and the control message must be type of
/// `SOL_SOCKET` and level of `SCM_CREDENTIALS`.
unsafe fn as_credentials(_data: &'a [u8]) -> Self {
CmsgData::ScmCredentials
}
fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Option<Self> {
unsafe {
let cmsg_len_zero = libc::CMSG_LEN(0) as usize;
let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero;
let data = libc::CMSG_DATA(cmsg);
let data = core::slice::from_raw_parts_mut(data, data_len);
match (*cmsg).cmsg_level {
libc::SOL_SOCKET => match (*cmsg).cmsg_type {
libc::SCM_RIGHTS => Some(CmsgData::as_rights(data)),
libc::SCM_CREDENTIALS => Some(CmsgData::as_credentials(data)),
_ => None,
},
_ => None,
}
}
}
}
/// The data unit of this control message is file descriptor(s).
///
/// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_RIGHTS`.
pub struct ScmRights<'a> {
data: &'a mut [u8],
}
impl<'a> ScmRights<'a> {
/// Iterate and reassign each fd in data buffer, given a reassignment function.
pub fn iter_and_reassign_fds<F>(&mut self, reassign_fd_fn: F)
where
F: Fn(FileDesc) -> FileDesc,
{
for fd_bytes in self.data.chunks_exact_mut(core::mem::size_of::<FileDesc>()) {
let old_fd = FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap());
let reassigned_fd = reassign_fd_fn(old_fd);
fd_bytes.copy_from_slice(&reassigned_fd.to_ne_bytes());
}
}
pub fn iter_fds(&self) -> impl Iterator<Item = FileDesc> + '_ {
self.data
.chunks_exact(core::mem::size_of::<FileDesc>())
.map(|fd_bytes| FileDesc::from_ne_bytes(fd_bytes.try_into().unwrap()))
}
}

@ -0,0 +1,34 @@
use num_enum::{IntoPrimitive, TryFromPrimitive};
/* Standard well-defined IP protocols. */
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(i32)]
pub enum SocketProtocol {
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */
IPPROTO_PUP = 12, /* PUP protocol. */
IPPROTO_UDP = 17, /* User Datagram Protocol. */
IPPROTO_IDP = 22, /* XNS IDP protocol. */
IPPROTO_TP = 29, /* SO Transport Protocol Class 4. */
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol. */
IPPROTO_IPV6 = 41, /* IPv6 header. */
IPPROTO_RSVP = 46, /* Reservation Protocol. */
IPPROTO_GRE = 47, /* General Routing Encapsulation. */
IPPROTO_ESP = 50, /* encapsulating security payload. */
IPPROTO_AH = 51, /* authentication header. */
IPPROTO_MTP = 92, /* Multicast Transport Protocol. */
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET. */
IPPROTO_ENCAP = 98, /* Encapsulation Header. */
IPPROTO_PIM = 103, /* Protocol Independent Multicast. */
IPPROTO_COMP = 108, /* Compression Header Protocol. */
IPPROTO_SCTP = 132, /* Stream Control Transmission Protocol. */
IPPROTO_UDPLITE = 136, /* UDP-Lite protocol. */
IPPROTO_MPLS = 137, /* MPLS in IP. */
IPPROTO_RAW = 255, /* Raw IP packets. */
IPPROTO_MAX,
}

@ -0,0 +1,34 @@
use crate::prelude::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum Shutdown {
Read = 0,
Write = 1,
Both = 2,
}
impl Shutdown {
pub fn from_c(c_val: u32) -> Result<Self> {
match c_val {
0 => Ok(Self::Read),
1 => Ok(Self::Write),
2 => Ok(Self::Both),
_ => return_errno!(EINVAL, "invalid how"),
}
}
pub fn to_c(&self) -> u32 {
*self as u32
}
pub fn should_shut_read(&self) -> bool {
// a slightly more efficient check than using two equality comparions
self.to_c() % 2 == 0
}
pub fn should_shut_write(&self) -> bool {
// a slightly more efficient check than using two equality comparions
self.to_c() >= 1
}
}

@ -0,0 +1,15 @@
use crate::prelude::*;
use num_enum::{IntoPrimitive, TryFromPrimitive};
/// A network type.
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(i32)]
pub enum Type {
STREAM = libc::SOCK_STREAM,
DGRAM = libc::SOCK_DGRAM,
RAW = libc::SOCK_RAW,
RDM = libc::SOCK_RDM,
SEQPACKET = libc::SOCK_SEQPACKET,
DCCP = libc::SOCK_DCCP,
PACKET = libc::SOCK_PACKET,
}