From 206239f9a28f8ee4f6c7d3cb4cd3d31060106da0 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Fri, 18 Jul 2025 04:48:37 +0000 Subject: [PATCH] feat: Implement Unix Domain Socket and related features. --- redox-rt/src/protocol.rs | 29 ++ redox-rt/src/sys.rs | 10 + src/header/sys_socket/constants.rs | 1 + src/header/sys_socket/mod.rs | 14 + src/header/sys_uio/mod.rs | 5 +- src/platform/redox/libredox.rs | 8 + src/platform/redox/socket.rs | 630 ++++++++++++++++++++++++++--- 7 files changed, 637 insertions(+), 60 deletions(-) diff --git a/redox-rt/src/protocol.rs b/redox-rt/src/protocol.rs index 0facb2592f..823b68c98f 100644 --- a/redox-rt/src/protocol.rs +++ b/redox-rt/src/protocol.rs @@ -37,6 +37,9 @@ pub enum ProcCall { Getppid = 12, Rename = 13, DisableSetpgid = 14, + + // Temporary calls for getting process credentials + GetProcCredentials = 15, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(usize)] @@ -47,6 +50,17 @@ pub enum ThreadCall { SignalThread = 1, } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(usize)] +pub enum SocketCall { + Bind = 0, + Connect = 1, + SetSockOpt = 2, + GetSockOpt = 3, + SendMsg = 4, + RecvMsg = 5, +} + impl ProcCall { pub fn try_from_raw(raw: usize) -> Option { Some(match raw { @@ -65,6 +79,7 @@ impl ProcCall { 12 => Self::Getppid, 13 => Self::Rename, 14 => Self::DisableSetpgid, + 15 => Self::GetProcCredentials, _ => return None, }) } @@ -79,6 +94,20 @@ impl ThreadCall { } } +impl SocketCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Bind, + 1 => Self::Connect, + 2 => Self::SetSockOpt, + 3 => Self::GetSockOpt, + 4 => Self::SendMsg, + 5 => Self::RecvMsg, + _ => return None, + }) + } +} + bitflags! { #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] pub struct WaitFlags: usize { diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 1fe71d8c4b..ada4c18646 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -296,6 +296,16 @@ pub fn posix_getresugid() -> Resugid { sgid, } } +pub fn get_proc_credentials(target_pid: usize, buf: &mut [u8]) -> Result { + if buf.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + this_proc_call( + buf, + CallFlags::empty(), + &[ProcCall::GetProcCredentials as u64, target_pid as u64], + ) +} pub fn posix_exit(status: i32) -> ! { this_proc_call( &mut [], diff --git a/src/header/sys_socket/constants.rs b/src/header/sys_socket/constants.rs index 89af160539..524ede079a 100644 --- a/src/header/sys_socket/constants.rs +++ b/src/header/sys_socket/constants.rs @@ -73,3 +73,4 @@ pub const SHUT_RDWR: c_int = 2; pub const SHUT_WR: c_int = 1; pub const SCM_RIGHTS: c_int = 1; +pub const SCM_CREDENTIALS: c_int = 2; diff --git a/src/header/sys_socket/mod.rs b/src/header/sys_socket/mod.rs index 527afa03ba..d7a68ab5b3 100644 --- a/src/header/sys_socket/mod.rs +++ b/src/header/sys_socket/mod.rs @@ -43,6 +43,14 @@ pub struct cmsghdr { pub cmsg_type: c_int, } +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ucred { + pub pid: pid_t, + pub uid: uid_t, + pub gid: gid_t, +} + #[no_mangle] pub extern "C" fn _cbindgen_export_cmsghdr(cmsghdr: cmsghdr) {} @@ -87,10 +95,12 @@ pub unsafe extern "C" fn __MHDR_END(mhdr: *const msghdr) -> *mut c_uchar { unsafe { ((*mhdr).msg_control as *mut c_uchar).offset((*mhdr).msg_controllen as isize) } } +#[no_mangle] pub unsafe extern "C" fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut c_uchar { unsafe { (cmsg as *mut c_uchar).offset(CMSG_ALIGN(mem::size_of::()) as isize) } } +#[no_mangle] pub unsafe extern "C" fn CMSG_NXTHDR(mhdr: *const msghdr, cmsg: *const cmsghdr) -> *mut cmsghdr { if cmsg.is_null() { return CMSG_FIRSTHDR(mhdr); @@ -109,6 +119,7 @@ pub unsafe extern "C" fn CMSG_NXTHDR(mhdr: *const msghdr, cmsg: *const cmsghdr) } } +#[no_mangle] pub unsafe extern "C" fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr { unsafe { if (*mhdr).msg_controllen as usize >= mem::size_of::() { @@ -119,14 +130,17 @@ pub unsafe extern "C" fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr { } } +#[no_mangle] pub unsafe extern "C" fn CMSG_ALIGN(len: size_t) -> size_t { (len + mem::size_of::() - 1) & !(mem::size_of::() - 1) } +#[no_mangle] pub unsafe extern "C" fn CMSG_SPACE(len: c_uint) -> c_uint { (CMSG_ALIGN(len as size_t) + CMSG_ALIGN(mem::size_of::())) as c_uint } +#[no_mangle] pub unsafe extern "C" fn CMSG_LEN(length: c_uint) -> c_uint { (CMSG_ALIGN(mem::size_of::()) + length as usize) as c_uint } diff --git a/src/header/sys_uio/mod.rs b/src/header/sys_uio/mod.rs index 418302acbf..c5dca07eed 100644 --- a/src/header/sys_uio/mod.rs +++ b/src/header/sys_uio/mod.rs @@ -11,9 +11,10 @@ use crate::{ pub const IOV_MAX: c_int = 1024; #[repr(C)] +#[derive(Debug)] pub struct iovec { - iov_base: *mut c_void, - iov_len: size_t, + pub iov_base: *mut c_void, + pub iov_len: size_t, } impl iovec { diff --git a/src/platform/redox/libredox.rs b/src/platform/redox/libredox.rs index bfeb4b81b6..64afa91cb3 100644 --- a/src/platform/redox/libredox.rs +++ b/src/platform/redox/libredox.rs @@ -241,6 +241,14 @@ pub unsafe extern "C" fn redox_get_egid_v1() -> RawResult { pub unsafe extern "C" fn redox_get_rgid_v1() -> RawResult { redox_rt::sys::posix_getresugid().rgid as _ } +#[no_mangle] +pub unsafe extern "C" fn redox_get_proc_credentials_v0( + target_pid: usize, + buf: &mut [u8], +) -> RawResult { + Error::mux(redox_rt::sys::get_proc_credentials(target_pid, buf)) +} + #[no_mangle] pub unsafe extern "C" fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult { Error::mux(redox_rt::sys::setrens(rns, ens).map(|()| 0)) diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs index 1e571f4bfb..9a906de48c 100644 --- a/src/platform/redox/socket.rs +++ b/src/platform/redox/socket.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use core::{cmp, mem, ptr, slice, str}; -use redox_rt::proc::FdGuard; +use redox_rt::{proc::FdGuard, protocol::SocketCall}; use syscall::{self, flag::*}; use super::{ @@ -11,13 +11,18 @@ use crate::{ error::{Errno, Result, ResultExt}, header::{ arpa_inet::inet_aton, - errno::{EAFNOSUPPORT, EDOM, EFAULT, EINVAL, ENOSYS, EOPNOTSUPP, EPROTONOSUPPORT}, + errno::{ + EAFNOSUPPORT, EDOM, EFAULT, EINVAL, EISCONN, EMSGSIZE, ENOMEM, ENOSYS, EOPNOTSUPP, + EPROTONOSUPPORT, + }, netinet_in::{in_addr, in_port_t, sockaddr_in}, string::strnlen, sys_socket::{ - constants::*, msghdr, sa_family_t, sockaddr, socklen_t, CMSG_FIRSTHDR, CMSG_NXTHDR, + cmsghdr, constants::*, msghdr, sa_family_t, sockaddr, socklen_t, ucred, CMSG_ALIGN, + CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, }, sys_time::timeval, + sys_uio::iovec, sys_un::sockaddr_un, }, }; @@ -63,35 +68,11 @@ macro_rules! bind_or_connect { path }, AF_UNIX => { - let data = &*($address as *const sockaddr_un); - - // NOTE: It's UB to access data in given address that exceeds - // the given address length. - - let maxlen = cmp::min( - // Max path length of the full-sized struct - data.sun_path.len(), - // Length inferred from given addrlen - $address_len as usize - data.path_offset() + // NOTE: bind/connect with AF_UNIX were replaced with SYS_CALL. + eprintln!( + "bind/connect with AF_UNIX were replaced with SYS_CALL." ); - let len = cmp::min( - // The maximum length of the address - maxlen, - // The first NUL byte, if any - strnlen(&data.sun_path as *const _, maxlen as size_t), - ); - - let addr = slice::from_raw_parts( - &data.sun_path as *const _ as *const u8, - len, - ); - let path = format!( - "{}", - str::from_utf8(addr).unwrap() - ); - trace!("path: {:?}", path); - - path + return Err(Errno(EAFNOSUPPORT)); }, _ => { return Err(Errno(EAFNOSUPPORT)); @@ -172,8 +153,18 @@ unsafe fn inner_get_name( // Format: [udp|tcp:]remote/local, chan:path let mut buf = [0; 256]; let len = syscall::fpath(socket as usize, &mut buf)?; - let buf = &buf[..len]; + inner_get_name_inner(local, address, address_len, &buf[..len]); + + Ok(()) +} + +unsafe fn inner_get_name_inner( + local: bool, + address: *mut sockaddr, + address_len: *mut socklen_t, + buf: &[u8], +) { if buf.starts_with(b"tcp:") || buf.starts_with(b"udp:") { inner_af_inet(local, &buf[4..], address, address_len); } else if buf.starts_with(b"/scheme/tcp/") || buf.starts_with(b"/scheme/udp/") { @@ -182,6 +173,10 @@ unsafe fn inner_get_name( inner_af_unix(&buf[5..], address, address_len); } else if buf.starts_with(b"/scheme/chan/") { inner_af_unix(&buf[13..], address, address_len); + } else if buf.starts_with(b"/scheme/uds_stream/") { + inner_af_unix(&buf[19..], address, address_len); + } else if buf.starts_with(b"/scheme/uds_dgram/") { + inner_af_unix(&buf[18..], address, address_len); } else { // Socket doesn't belong to any scheme panic!( @@ -189,8 +184,6 @@ unsafe fn inner_get_name( str::from_utf8(buf) ); } - - Ok(()) } fn socket_kind(mut kind: c_int) -> (c_int, usize) { @@ -206,6 +199,291 @@ fn socket_kind(mut kind: c_int) -> (c_int, usize) { (kind, flags) } +unsafe fn serialize_payload_to_stream( + msg_stream: &mut Vec, + iovs: &[iovec], + whole_iov_size: usize, +) -> Result { + msg_stream.extend_from_slice(&whole_iov_size.to_le_bytes()); + + for iov in iovs { + if iov.iov_len > 0 { + if iov.iov_base.is_null() { + return Err(Errno(EFAULT)); + } + let source_slice: &[u8] = + unsafe { slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len) }; + msg_stream.extend_from_slice(source_slice); + } + } + Ok(whole_iov_size) +} + +unsafe fn serialize_ancillary_data_to_stream( + msg: *const msghdr, + mhdr: &msghdr, + socket: c_int, + msg_stream: &mut Vec, +) -> Result<()> { + if mhdr.msg_control.is_null() { + return Err(Errno(EINVAL)); + } + + let mut cmsg: *mut cmsghdr = CMSG_FIRSTHDR(msg); + let mut cmsg_count = 0; + while !cmsg.is_null() { + cmsg_count += 1; + let current_cmsg = &*cmsg; + let min_cmsg_len = CMSG_ALIGN(mem::size_of::()); + if current_cmsg.cmsg_len < min_cmsg_len { + return Err(Errno(EINVAL)); + } + + // cmsg entry format: [level(i32)][type(i32)][data_len(usize)][data] + msg_stream.extend_from_slice(¤t_cmsg.cmsg_level.to_le_bytes()); + msg_stream.extend_from_slice(¤t_cmsg.cmsg_type.to_le_bytes()); + + match (current_cmsg.cmsg_level, current_cmsg.cmsg_type) { + (SOL_SOCKET, SCM_RIGHTS) => { + let data_len = current_cmsg.cmsg_len - min_cmsg_len; + if data_len % mem::size_of::() != 0 { + return Err(Errno(EINVAL)); + } + let fd_count = data_len / mem::size_of::(); + + // Call syscall::sendfd for each fd. + if fd_count > 0 { + let fds_ptr = CMSG_DATA(cmsg) as *const c_int; + let fds_slice = slice::from_raw_parts(fds_ptr, fd_count); + for &fd in fds_slice.iter() { + let fd_to_send = FdGuard::new(syscall::dup(fd as usize, b"")?); + syscall::sendfd(socket as usize, *fd_to_send as usize, 0, 0)?; + } + } + + // Serialize to ancillary_data_stream. + // Our intermediate format: data_len is size of fd_count (usize), data is fd_count (usize) + let data_for_stream_len = mem::size_of::(); + let data_for_stream_payload = (fd_count as usize).to_le_bytes(); + + msg_stream.extend_from_slice(&(data_for_stream_len as usize).to_le_bytes()); + msg_stream.extend_from_slice(&data_for_stream_payload); + } + (SOL_SOCKET, SCM_CREDENTIALS) => { + // Our intermediate format: data_len is 0, no data payload + let data_for_stream_len = 0usize; + msg_stream.extend_from_slice(&(data_for_stream_len as usize).to_le_bytes()); + } + _ => { + return Err(Errno(EOPNOTSUPP)); + } + } + cmsg = CMSG_NXTHDR(msg, cmsg); + } + Ok(()) +} + +unsafe fn deserialize_name_from_stream( + mhdr: &mut msghdr, + msg_stream: &[u8], + cursor: &mut usize, +) -> Result<()> { + // Read name_len from stream + let name_len_in_stream = read_num::(&msg_stream[*cursor..])?; + let name_len = cmp::min(name_len_in_stream, mhdr.msg_namelen as usize); + *cursor += mem::size_of::(); + + if name_len > 0 { + if *cursor + name_len > msg_stream.len() { + return Err(Errno(EMSGSIZE)); + } + if !mhdr.msg_name.is_null() && mhdr.msg_namelen > 0 { + let name_buffer = &msg_stream[*cursor..*cursor + name_len]; + inner_get_name_inner( + false, + mhdr.msg_name as *mut sockaddr, + &mut mhdr.msg_namelen, + name_buffer, + ); + } + *cursor += name_len; + } else { + // If name_len is 0, set msg_namelen to 0 + mhdr.msg_namelen = 0; + } + Ok(()) +} + +unsafe fn deserialize_payload_from_stream( + mhdr: &mut msghdr, + msg_stream: &[u8], + iovs: &[iovec], + whole_iov_size: usize, + cursor: &mut usize, + test: u8, +) -> Result { + let full_payload_len_from_scheme = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + // Determine actual payload data available in the stream + let payload_len_to_read = cmp::min(full_payload_len_from_scheme, whole_iov_size); + let payload_data_from_stream = &msg_stream[*cursor..*cursor + payload_len_to_read]; + *cursor += payload_len_to_read; + + let mut total_bytes_written: usize = 0; + if !iovs.is_empty() && payload_len_to_read > 0 { + let mut source_bytes_consumed: usize = 0; + for iov in iovs { + if iov.iov_len == 0 { + continue; + } + if iov.iov_base.is_null() { + return Err(Errno(EFAULT)); + } + + let source_bytes_remaining = payload_data_from_stream + .len() + .saturating_sub(source_bytes_consumed); + if source_bytes_remaining == 0 { + break; + } + + let bytes_to_write = cmp::min(iov.iov_len, source_bytes_remaining); + if bytes_to_write > 0 { + let dest_slice: &mut [u8] = + unsafe { slice::from_raw_parts_mut(iov.iov_base as *mut u8, iov.iov_len) }; + + let source_sub_slice = &payload_data_from_stream + [source_bytes_consumed..source_bytes_consumed + bytes_to_write]; + dest_slice[..bytes_to_write].copy_from_slice(source_sub_slice); + total_bytes_written += bytes_to_write; + source_bytes_consumed += bytes_to_write; + } + } + } + + if full_payload_len_from_scheme > whole_iov_size { + mhdr.msg_flags |= MSG_TRUNC; + } + + Ok(total_bytes_written) +} + +unsafe fn deserialize_ancillary_data_from_stream( + mhdr: &mut msghdr, + socket: c_int, + msg_stream: &[u8], + cursor: &mut usize, + cmsg_space_provided: usize, +) -> Result<()> { + let mut current_cmsg_ptr_in_user_buf = if !mhdr.msg_control.is_null() && cmsg_space_provided > 0 + { + CMSG_FIRSTHDR(mhdr) + } else { + ptr::null_mut() + }; + let mut remaining_user_cmsg_buf_len = cmsg_space_provided; + let mut total_csmg_bytes_written_to_user_buf: usize = 0; + + while *cursor < msg_stream.len() { + const CMSG_HEADER_LEN_IN_STREAM: usize = + mem::size_of::() * 2 + mem::size_of::(); + if *cursor + CMSG_HEADER_LEN_IN_STREAM > msg_stream.len() { + if msg_stream[*cursor..].iter().any(|&b| b != 0) { + mhdr.msg_flags |= MSG_CTRUNC; + } + break; + } + + // cmsg entry format: [level(i32)][type(i32)][data_len(usize)][data] + let cmsg_level = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + let cmsg_type = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + let cmsg_data_len_in_stream = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + + if *cursor + cmsg_data_len_in_stream > msg_stream.len() { + mhdr.msg_flags |= MSG_CTRUNC; + break; + } + + let cmsg_data_from_stream = &msg_stream[*cursor..*cursor + cmsg_data_len_in_stream]; + *cursor += cmsg_data_len_in_stream; + + let mut actual_posix_cmsg_data_len: usize = 0; + let mut temp_posix_cmsg_data_buf: Vec = Vec::new(); + + match (cmsg_level, cmsg_type) { + (SOL_SOCKET, SCM_RIGHTS) => { + if cmsg_data_len_in_stream != mem::size_of::() { + return Err(Errno(EINVAL)); + } + let fd_count = read_num::(&cmsg_data_from_stream)?; + + for _ in 0..fd_count { + // Call syscall::dup to duplicate the fd + let new_fd = syscall::dup(socket as usize, b"recvfd")?; + temp_posix_cmsg_data_buf.extend_from_slice(&(new_fd as c_int).to_le_bytes()); + } + actual_posix_cmsg_data_len = temp_posix_cmsg_data_buf.len(); + } + (SOL_SOCKET, SCM_CREDENTIALS) => { + if cmsg_data_len_in_stream + != mem::size_of::() + mem::size_of::() + mem::size_of::() + { + return Err(Errno(EINVAL)); + } + + let pid = read_num::(&cmsg_data_from_stream)?; + let uid_offset = mem::size_of::(); + let uid = read_num::(&cmsg_data_from_stream[uid_offset..])?; + let gid_offset = uid_offset + mem::size_of::(); + let gid = read_num::(&cmsg_data_from_stream[gid_offset..])?; + let cred = ucred { pid, uid, gid }; + + temp_posix_cmsg_data_buf.extend_from_slice(unsafe { + slice::from_raw_parts( + &cred as *const ucred as *const u8, + mem::size_of::(), + ) + }); + actual_posix_cmsg_data_len = temp_posix_cmsg_data_buf.len(); + } + _ => { + return Err(Errno(EINVAL)); + } + } + + let space_needed_for_posix_cmsg = CMSG_SPACE(actual_posix_cmsg_data_len as u32) as usize; + + if !current_cmsg_ptr_in_user_buf.is_null() + && remaining_user_cmsg_buf_len >= space_needed_for_posix_cmsg + { + let cmsg_ref = &mut *current_cmsg_ptr_in_user_buf; + cmsg_ref.cmsg_len = CMSG_LEN(actual_posix_cmsg_data_len as u32) as usize; + cmsg_ref.cmsg_level = cmsg_level; + cmsg_ref.cmsg_type = cmsg_type; + + let data_ptr_in_user_cmsg = CMSG_DATA(cmsg_ref); + ptr::copy_nonoverlapping( + temp_posix_cmsg_data_buf.as_ptr(), + data_ptr_in_user_cmsg as *mut u8, + actual_posix_cmsg_data_len, + ); + + let aligned_len_written = CMSG_ALIGN(cmsg_ref.cmsg_len); + total_csmg_bytes_written_to_user_buf += aligned_len_written; + remaining_user_cmsg_buf_len -= aligned_len_written; + current_cmsg_ptr_in_user_buf = CMSG_NXTHDR(mhdr, current_cmsg_ptr_in_user_buf); + } else { + mhdr.msg_flags |= MSG_CTRUNC; + break; + } + } + mhdr.msg_controllen = total_csmg_bytes_written_to_user_buf; + Ok(()) +} + impl PalSocket for Sys { unsafe fn accept( socket: c_int, @@ -220,7 +498,45 @@ impl PalSocket for Sys { } unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()> { - bind_or_connect!(bind into, socket, address, address_len)?; + match (*address).sa_family as c_int { + AF_INET => { + bind_or_connect!(bind into, socket, address, address_len)?; + } + AF_UNIX => { + let data = &*(address as *const sockaddr_un); + + // NOTE: It's UB to access data in given address that exceeds + // the given address length. + + let maxlen = cmp::min( + // Max path length of the full-sized struct + data.sun_path.len(), + // Length inferred from given addrlen + address_len as usize - data.path_offset(), + ); + let len = cmp::min( + // The maximum length of the address + maxlen, + // The first NUL byte, if any + strnlen(&data.sun_path as *const _, maxlen as size_t), + ); + + let addr = slice::from_raw_parts(&data.sun_path as *const _ as *const u8, len); + let mut path = format!("{}", str::from_utf8(addr).unwrap()); + trace!("path: {:?}", path); + + redox_rt::sys::sys_call( + socket as usize, + path.as_bytes_mut(), + CallFlags::empty(), + &[SocketCall::Bind as u64], + )?; + } + _ => { + return Err(Errno(EAFNOSUPPORT)); + } + }; + Ok(()) } @@ -229,7 +545,41 @@ impl PalSocket for Sys { address: *const sockaddr, address_len: socklen_t, ) -> Result { - bind_or_connect!(connect into, socket, address, address_len) + match (*address).sa_family as c_int { + AF_INET => bind_or_connect!(bind into, socket, address, address_len), + AF_UNIX => { + let data = &*(address as *const sockaddr_un); + + // NOTE: It's UB to access data in given address that exceeds + // the given address length. + + let maxlen = cmp::min( + // Max path length of the full-sized struct + data.sun_path.len(), + // Length inferred from given addrlen + address_len as usize - data.path_offset(), + ); + let len = cmp::min( + // The maximum length of the address + maxlen, + // The first NUL byte, if any + strnlen(&data.sun_path as *const _, maxlen as size_t), + ); + + let addr = slice::from_raw_parts(&data.sun_path as *const _ as *const u8, len); + let mut path = format!("{}", str::from_utf8(addr).unwrap()); + trace!("path: {:?}", path); + + redox_rt::sys::sys_call( + socket as usize, + path.as_bytes_mut(), + CallFlags::empty(), + &[SocketCall::Connect as u64], + )?; + Result::::Ok(0) + } + _ => Err(Errno(EAFNOSUPPORT)), + } } unsafe fn getpeername( @@ -311,29 +661,134 @@ impl PalSocket for Sys { } unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result { - //TODO: implement recvfrom with recvmsg - eprintln!( - "recvmsg({}, {:p}, {:#x}) not implemented on redox", - socket, msg, flags - ); - if !msg.is_null() { - let msg = &*msg; - eprintln!("{:#x?}", msg); - if !msg.msg_control.is_null() { - let mut cmsg = CMSG_FIRSTHDR(msg); - while !cmsg.is_null() { - eprintln!("{:#x?}", *cmsg); - cmsg = CMSG_NXTHDR(msg, cmsg); - } - } + if msg.is_null() { + return Err(Errno(EINVAL)); } - Err(Errno(ENOSYS)) + let mut mhdr = &mut *msg; + let iovs_slice: &[iovec] = if mhdr.msg_iov.is_null() || mhdr.msg_iovlen == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(mhdr.msg_iov, mhdr.msg_iovlen as usize) } + }; + let whole_iov_size: usize = iovs_slice.iter().map(|iov| iov.iov_len).sum(); + + let mut msg_stream: Vec = Vec::new(); + + // Prepare space for the message stream. + // [name_len(usize)][name_buffer] + // [payload_len(usize)][payload_data_buffer] + // [ancillary_stream_buffer] + let expected_stream_size = { + mem::size_of::() // name_len + + mhdr.msg_namelen as usize // name_buffer + + mem::size_of::() // payload_len + + whole_iov_size // payload_data_buffer + + mhdr.msg_controllen as usize // ancillary_stream_buffer + }; + msg_stream + .try_reserve_exact(expected_stream_size) + .map_err(|_| Errno(ENOMEM))?; + msg_stream.resize(expected_stream_size, 0); + + // Write the information about the msghdr + let mut cursor: usize = 0; + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(mhdr.msg_namelen as usize).to_le_bytes()); + cursor += mem::size_of::(); + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(whole_iov_size).to_le_bytes()); + cursor += mem::size_of::(); + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(mhdr.msg_controllen as usize).to_le_bytes()); + + // Read the message stream. + let metadata = [SocketCall::RecvMsg as u64]; + let call_flags = CallFlags::empty(); + let actual_read_len = + redox_rt::sys::sys_call(socket as usize, &mut msg_stream, call_flags, &metadata)?; + msg_stream.truncate(actual_read_len); + + cursor = 0; + let cmsg_space_provided_by_user = mhdr.msg_controllen; + mhdr.msg_flags = 0; + + // Read sender name. + deserialize_name_from_stream(&mut mhdr, &msg_stream, &mut cursor)?; + + // Read payload data. + let actual_payload_bytes_written_to_iov = deserialize_payload_from_stream( + &mut mhdr, + &msg_stream, + iovs_slice, + whole_iov_size, + &mut cursor, + 0u8, + )?; + + // Reconstruct the ancillary data in the user-provided buffer. + let has_cmsg_buffer = !mhdr.msg_control.is_null() && cmsg_space_provided_by_user > 0; + let has_ancillary_data = cursor < msg_stream.len(); + if has_cmsg_buffer && has_ancillary_data { + deserialize_ancillary_data_from_stream( + mhdr, + socket, + &msg_stream, + &mut cursor, + cmsg_space_provided_by_user as usize, + )?; + } else { + mhdr.msg_controllen = 0; // No ancillary data + } + Ok(actual_payload_bytes_written_to_iov) } unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result { - //TODO: implement sendto with sendmsg - eprintln!("sendmsg not implemented on redox"); - Err(Errno(ENOSYS)) + if msg.is_null() { + return Err(Errno(EINVAL)); + } + let mhdr = &*msg; + + // Reserve space for the message stream. + // [payload_len(usize)][payload_data_buffer] + // [ancillary_stream_buffer] + let iovs_slice: &[iovec] = if mhdr.msg_iov.is_null() || mhdr.msg_iovlen == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(mhdr.msg_iov, mhdr.msg_iovlen as usize) } + }; + + let mut msg_stream: Vec = Vec::new(); + let whole_iov_size: usize = iovs_slice.iter().map(|iov| iov.iov_len).sum(); + msg_stream + .try_reserve_exact( + mem::size_of::() // payload_len + + whole_iov_size // payload_data_buffer + + mhdr.msg_controllen as usize, // ancillary_stream_buffer + ) + .map_err(|_| Errno(ENOMEM))?; + + // Write the message to the msg_stream. + let mut actual_payload_bytes_serialized = 0; + if !mhdr.msg_iov.is_null() && mhdr.msg_iovlen > 0 { + actual_payload_bytes_serialized = + serialize_payload_to_stream(&mut msg_stream, &iovs_slice, whole_iov_size)?; + } + // Process Control Messages from msghdr and serialize them. + if mhdr.msg_controllen > 0 { + serialize_ancillary_data_to_stream(msg, mhdr, socket, &mut msg_stream)?; + } + + // Send the message stream. + let metadata = [SocketCall::SendMsg as u64]; + let call_flags = CallFlags::empty(); + let written = redox_rt::sys::sys_call( + socket as usize, + msg_stream.as_mut_slice(), + call_flags, + &metadata, + )?; + + Ok(actual_payload_bytes_serialized) } unsafe fn sendto( @@ -392,6 +847,19 @@ impl PalSocket for Sys { SOL_SOCKET => match option_name { SO_RCVTIMEO => return set_timeout(b"read_timeout"), SO_SNDTIMEO => return set_timeout(b"write_timeout"), + SO_PASSCRED => { + let metadata = [SocketCall::SetSockOpt as u64, option_name as u64]; + let payload = + slice::from_raw_parts_mut(option_value as *mut u8, option_len as usize); + let call_flags = CallFlags::empty(); + redox_rt::sys::sys_call( + socket as usize, + payload, + CallFlags::empty(), + &metadata, + )?; + return Ok(()); + } _ => (), }, _ => (), @@ -425,7 +893,10 @@ impl PalSocket for Sys { Ok(match (domain, kind) { (AF_INET, SOCK_STREAM) => syscall::open("/scheme/tcp", flags)? as c_int, (AF_INET, SOCK_DGRAM) => syscall::open("/scheme/udp", flags)? as c_int, - (AF_UNIX, SOCK_STREAM) => syscall::open("/scheme/chan", flags | O_CREAT)? as c_int, + (AF_UNIX, SOCK_STREAM) => { + syscall::open("/scheme/uds_stream", flags | O_CREAT)? as c_int + } + (AF_UNIX, SOCK_DGRAM) => syscall::open("/scheme/uds_dgram", flags | O_CREAT)? as c_int, _ => return Err(Errno(EPROTONOSUPPORT)), }) } @@ -435,9 +906,9 @@ impl PalSocket for Sys { match (domain, kind) { (AF_UNIX, SOCK_STREAM) => { - let listener = FdGuard::new(syscall::open("/scheme/chan", flags | O_CREAT)?); + let listener = FdGuard::new(syscall::open("/scheme/uds_stream", flags | O_CREAT)?); - // For now, chan: lets connects be instant, and instead blocks + // For now, uds_stream: lets connects be instant, and instead blocks // on any I/O performed. So we don't need to mark this as // nonblocking. @@ -449,6 +920,19 @@ impl PalSocket for Sys { sv[1] = fd1.take() as c_int; Ok(()) } + (AF_UNIX, SOCK_DGRAM) => { + let listener = FdGuard::new(syscall::open("/scheme/uds_dgram", flags | O_CREAT)?); + + // For now, uds_dgram: lets connects be instant, and instead blocks + // on any I/O performed. So we don't need to mark this as + // nonblocking. + + let mut fd0 = FdGuard::new(syscall::dup(*listener, b"connect")?); + + sv[0] = fd0.take() as c_int; + sv[1] = listener.take() as c_int; + Ok(()) + } _ => unsafe { eprintln!( "socketpair({}, {}, {}, {:p})", @@ -462,3 +946,33 @@ impl PalSocket for Sys { } } } + +fn read_num(buffer: &[u8]) -> Result +where + T: NumFromBytes, +{ + T::from_le_bytes_slice(buffer) +} +trait NumFromBytes: Sized { + fn from_le_bytes_slice(buffer: &[u8]) -> Result; +} +impl NumFromBytes for i32 { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + Ok(i32::from_le_bytes( + buffer + .get(..mem::size_of::()) + .and_then(|slice| slice.try_into().ok()) + .ok_or_else(|| Errno(EFAULT))?, + )) + } +} +impl NumFromBytes for usize { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + Ok(usize::from_le_bytes( + buffer + .get(..mem::size_of::()) + .and_then(|slice| slice.try_into().ok()) + .ok_or_else(|| Errno(EFAULT))?, + )) + } +}