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,