From c9653b50ebe4c0f17b2b628eead93402df07580a Mon Sep 17 00:00:00 2001 From: Benton60 Date: Wed, 18 Mar 2026 21:00:10 -0400 Subject: [PATCH 1/5] initial setup for recvmsg, all handlers return EOPNOTSUPP for now --- netstack/src/scheme/icmp.rs | 4 ++++ netstack/src/scheme/ip.rs | 4 ++++ netstack/src/scheme/socket.rs | 24 +++++++++++++++++++++++- netstack/src/scheme/tcp.rs | 4 ++++ netstack/src/scheme/udp.rs | 4 ++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/netstack/src/scheme/icmp.rs b/netstack/src/scheme/icmp.rs index c2b1b5d0c6..11330799f7 100644 --- a/netstack/src/scheme/icmp.rs +++ b/netstack/src/scheme/icmp.rs @@ -281,6 +281,10 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { } } + fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { + return Err(SyscallError::new(syscall::EOPNOTSUPP)); + } + fn handle_get_peer_name( &self, file: &SchemeFile, diff --git a/netstack/src/scheme/ip.rs b/netstack/src/scheme/ip.rs index f39d00a638..96eb139916 100644 --- a/netstack/src/scheme/ip.rs +++ b/netstack/src/scheme/ip.rs @@ -149,6 +149,10 @@ impl<'a> SchemeSocket for RawSocket<'a> { Ok(i) } + fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { + return Err(SyscallError::new(syscall::EOPNOTSUPP)); + } + fn handle_get_peer_name( &self, file: &SchemeFile, diff --git a/netstack/src/scheme/socket.rs b/netstack/src/scheme/socket.rs index a99a696a79..9f832a6660 100644 --- a/netstack/src/scheme/socket.rs +++ b/netstack/src/scheme/socket.rs @@ -243,6 +243,13 @@ where fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult; + fn handle_recvmsg( + &mut self, + file: &mut SchemeFile, + payload: &mut [u8], + flags: usize, + ) -> SyscallResult; + fn get_sock_opt( &self, file: &SchemeFile, @@ -496,7 +503,6 @@ where } } // SocketCall::SendMsg => self.handle_sendmsg(id, payload, ctx), - // SocketCall::RecvMsg => self.handle_recvmsg(id, payload), // SocketCall::Unbind => self.handle_unbind(id), // SocketCall::GetToken => self.handle_get_token(id, payload), SocketCall::GetPeerName => { @@ -514,6 +520,22 @@ where SocketT::handle_get_peer_name(socket, file, payload) } + SocketCall::RecvMsg => { + let flags = metadata[1] as usize; + let handle = self.handles.get_mut(&fd).ok_or_else(||SyscallError::new(syscall::EBADF))?; + + match *handle { + Handle::File(ref mut file) => { + let mut socket_set = self.socket_set.borrow_mut(); + let socket = socket_set.get_mut::(file.socket_handle()); + + SocketT::handle_recvmsg(socket, file, payload, flags) + } + Handle::Null(_) => Err(SyscallError::new(syscall::EINVAL)), + Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)), + } + } + SocketCall::Shutdown => { let how = metadata[1] as usize; diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index 8c46eec49c..225d5f2af2 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -308,6 +308,10 @@ impl<'a> SchemeSocket for TcpSocket<'a> { Ok(i) } + fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { + return Err(SyscallError::new(syscall::EOPNOTSUPP)); + } + fn handle_get_peer_name( &self, file: &SchemeFile, diff --git a/netstack/src/scheme/udp.rs b/netstack/src/scheme/udp.rs index b0ad69bbf5..0dc50f0f0f 100644 --- a/netstack/src/scheme/udp.rs +++ b/netstack/src/scheme/udp.rs @@ -296,6 +296,10 @@ impl<'a> SchemeSocket for UdpSocket<'a> { Ok(i) } + fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { + return Err(SyscallError::new(syscall::EOPNOTSUPP)); + } + fn handle_get_peer_name( &self, file: &SchemeFile, From f8eff8bec341f6c0a914da84aafaece4dbd856d8 Mon Sep 17 00:00:00 2001 From: Benton60 Date: Wed, 18 Mar 2026 21:03:53 -0400 Subject: [PATCH 2/5] implement the recvmsg handler for udp --- netstack/src/scheme/udp.rs | 53 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/netstack/src/scheme/udp.rs b/netstack/src/scheme/udp.rs index 0dc50f0f0f..8cb1292c1b 100644 --- a/netstack/src/scheme/udp.rs +++ b/netstack/src/scheme/udp.rs @@ -12,6 +12,7 @@ use super::{parse_endpoint, SchemeWrapper, Smolnetd, SocketSet}; use crate::port_set::PortSet; use crate::router::Router; use std::fmt::Write; +use libredox::flag; const SO_SNDBUF: usize = 7; const SO_RCVBUF: usize = 8; @@ -59,7 +60,7 @@ impl<'a> SchemeSocket for UdpSocket<'a> { if !is_broadcast && !connected_addr.is_unspecified() { if source.addr != connected_addr || source.port != data.port { - // Bad packet detetced + // Bad packet detected // Remove it from the buffer immediately so poll() doesn't trigger let _ = self.recv(); continue; // Loop again to check the next packet @@ -297,9 +298,57 @@ impl<'a> SchemeSocket for UdpSocket<'a> { } fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { - return Err(SyscallError::new(syscall::EOPNOTSUPP)); + //there is a separate flags argument for MSG_DONTWAIT which is call specific not socket-wide like socket_file.flags + let socket_file = match file{ + SchemeFile::Socket(ref mut sock_f) => sock_f, + _ => return Err(SyscallError::new(syscall::EBADF)) + }; + + if !socket_file.read_enabled { + Ok(0) + } else if self.can_recv(&socket_file.data) { + let usize_length = core::mem::size_of::(); + let prepared_name_len = usize::from_le_bytes( + how[0..usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + let prepared_whole_iov_size = usize::from_le_bytes( + how[usize_length..2 * usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + let prepared_msg_controllen = usize::from_le_bytes( + how[2 * usize_length..3 * usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + if prepared_name_len + prepared_msg_controllen + prepared_whole_iov_size > how.len() {//expected returned buffer size is larger than provided -> return invalid + return Err(SyscallError::new(syscall::EINVAL)); + } + + //the relibc deserialization functions expect NO GAPS between the name and payload slices + //so the payload must be temporarily stored during recv_slice + let mut payload_tmp = vec![0u8; prepared_whole_iov_size - prepared_name_len]; + let (length, address) = self.recv_slice(&mut payload_tmp).expect("Can't recieve slice"); + + //Address Handling + let address_formatted = if prepared_name_len > 0 { + format!("/scheme/udp/{}:{}", address.endpoint.addr, address.endpoint.port) + } else { + String::from("") + }; + how[..usize_length].copy_from_slice(&address_formatted.len().to_le_bytes()); + let payload_len_index = address_formatted.len() + usize_length; + how[usize_length..payload_len_index].copy_from_slice(&address_formatted.as_bytes()); + + //Payload Handling + how[payload_len_index..payload_len_index + usize_length].copy_from_slice(&(length as usize).to_le_bytes()); + how[payload_len_index + usize_length..payload_len_index + usize_length + length].copy_from_slice(&payload_tmp[..length]); + //TODO should return the length of payload but relibc uses trim on the buffer incorrectly + Ok(how.len()) + } else if socket_file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK || flags & flag::MSG_DONTWAIT as usize != 0 { + Err(SyscallError::new(syscall::EAGAIN)) + } else { + Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read + } } + fn handle_get_peer_name( &self, file: &SchemeFile, From 0ad36bf5b4bc409318d61fb1162d5d7f0c008fc5 Mon Sep 17 00:00:00 2001 From: Benton60 Date: Wed, 18 Mar 2026 21:08:48 -0400 Subject: [PATCH 3/5] implement recvmsg handler for tcp --- netstack/src/scheme/tcp.rs | 42 +++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index 225d5f2af2..583011197e 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -9,6 +9,7 @@ use syscall::{Error as SyscallError, Result as SyscallResult}; use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile}; use super::{parse_endpoint, SchemeWrapper, SocketSet}; use crate::port_set::PortSet; +use libredox::flag; const SO_SNDBUF: usize = 7; const SO_RCVBUF: usize = 8; @@ -309,9 +310,48 @@ impl<'a> SchemeSocket for TcpSocket<'a> { } fn handle_recvmsg(&mut self, file: &mut SchemeFile, how:&mut [u8], flags:usize) -> SyscallResult { - return Err(SyscallError::new(syscall::EOPNOTSUPP)); + let socket_file = match file { + SchemeFile::Socket(ref mut sock_f) => sock_f, + _ => return Err(SyscallError::new(syscall::EBADF)) + }; + if !socket_file.read_enabled { + Ok(0) + } else if self.can_recv(&socket_file.data) { + let usize_length = core::mem::size_of::(); + let prepared_name_len = usize::from_le_bytes( + how[0..usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + let prepared_whole_iov_size = usize::from_le_bytes( + how[usize_length..2 * usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + let prepared_msg_controllen = usize::from_le_bytes( + how[2 * usize_length..3 * usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?, + ); + if prepared_name_len + prepared_msg_controllen + prepared_whole_iov_size > how.len() {//expected returned buffer size is larger than provided -> return invalid + return Err(SyscallError::new(syscall::EINVAL)); + } + let mut address = match self.remote_endpoint() { + Some(endpoint) => format!("/scheme/tcp/{}:{}", endpoint.addr, endpoint.port), + None => String::from("/scheme/tcp/0.0.0.0:0"), + }; + + + how[..usize_length].copy_from_slice(&address.len().to_le_bytes()); + how[usize_length..address.len() + usize_length].copy_from_slice(&address.as_bytes()); + let payload_length = self.recv_slice(&mut how[address.len() + 2 * usize_length..address.len() + 2 * usize_length + prepared_whole_iov_size]).unwrap(); + println!("\n\n{}\n", payload_length); + how[address.len() + usize_length..address.len() + 2 * usize_length].copy_from_slice(&payload_length.to_le_bytes()); + //TODO should return the length of payload but relibc uses trim on the buffer incorrectly + Ok(how.len()) + + } else if socket_file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK || flags & flag::MSG_DONTWAIT as usize != 0 { + Err(SyscallError::new(syscall::EAGAIN)) + } else { + Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read + } } + fn handle_get_peer_name( &self, file: &SchemeFile, From 87bda3ec3245e756e076a2e80801529c77156238 Mon Sep 17 00:00:00 2001 From: Benton60 Date: Wed, 18 Mar 2026 21:11:26 -0400 Subject: [PATCH 4/5] udpate the fpath return value to reflect the new format --- netstack/src/scheme/icmp.rs | 4 ++-- netstack/src/scheme/ip.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/netstack/src/scheme/icmp.rs b/netstack/src/scheme/icmp.rs index 11330799f7..f1005e6452 100644 --- a/netstack/src/scheme/icmp.rs +++ b/netstack/src/scheme/icmp.rs @@ -252,7 +252,7 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { if let SchemeFile::Socket(ref socket_file) = *file { match socket_file.data.socket_type { IcmpSocketType::Echo => { - let path = format!("icmp:echo/{}", socket_file.data.ip); + let path = format!("/scheme/icmp/echo/{}", socket_file.data.ip); let path = path.as_bytes(); let mut i = 0; @@ -264,7 +264,7 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { Ok(i) } IcmpSocketType::Udp => { - let path = format!("icmp:udp/{}", socket_file.data.ip); + let path = format!("/scheme/icmp/udp/{}", socket_file.data.ip); let path = path.as_bytes(); let mut i = 0; diff --git a/netstack/src/scheme/ip.rs b/netstack/src/scheme/ip.rs index 96eb139916..6108cea7a4 100644 --- a/netstack/src/scheme/ip.rs +++ b/netstack/src/scheme/ip.rs @@ -137,7 +137,7 @@ impl<'a> SchemeSocket for RawSocket<'a> { } fn fpath(&self, _file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { - let path = format!("ip:{}", self.ip_protocol()); + let path = format!("/scheme/ip/{}", self.ip_protocol()); let path = path.as_bytes(); let mut i = 0; From 259f78ae7c1fdeb7b78fcdb4c12ba708e07f68e9 Mon Sep 17 00:00:00 2001 From: Benton60 Date: Tue, 24 Mar 2026 17:26:09 -0400 Subject: [PATCH 5/5] bump the libredox version --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 657622ddb9..31330c563b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,9 +1186,9 @@ checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libredox" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" dependencies = [ "bitflags 2.11.0", "ioslice", diff --git a/Cargo.toml b/Cargo.toml index 3b2f5247db..9bf3ac5f00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ edid = "0.3.0" #TODO: edid is abandoned, fork it and maintain? fdt = "0.1.5" libc = "0.2.181" log = "0.4" -libredox = "0.1.14" +libredox = "0.1.15" orbclient = "0.3.51" parking_lot = "0.12" pico-args = "0.5"