udp: sendmsg/sendto support for unconnected UDP sockets
SchemeSocket trait gains handle_sendmsg(file, payload, flags) with default EOPNOTSUPP. SocketScheme::call_inner now dispatches SocketCall::SendMsg (previously commented out). UDP handle_sendmsg parses the sendmsg buffer format: [name_len: usize][payload_len: usize][msg_controllen: usize] [address: bytes][payload: bytes] Extracts the destination IP:port from the address, sends via smoltcp's send_slice. Enables sendto() on unconnected UDP sockets. Unconnected UDP write_buf now returns EDESTADDRREQ instead of EADDRNOTAVAIL — clearer error for unconnected writes without sendto. TCP and ICMP use the default EOPNOTSUPP implementation (they are connection-oriented or use write_buf). All 31 tests pass.
This commit is contained in:
@@ -251,6 +251,15 @@ where
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize>;
|
||||
|
||||
fn handle_sendmsg(
|
||||
&mut self,
|
||||
_file: &mut SchemeFile<Self>,
|
||||
_payload: &[u8],
|
||||
_flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
Err(SyscallError::new(syscall::EOPNOTSUPP))
|
||||
}
|
||||
|
||||
fn get_sock_opt(
|
||||
&self,
|
||||
_file: &SchemeFile<Self>,
|
||||
@@ -506,7 +515,21 @@ where
|
||||
Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
// SocketCall::SendMsg => self.handle_sendmsg(id, payload, ctx),
|
||||
SocketCall::SendMsg => {
|
||||
let flags = metadata[1] as usize;
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match *handle {
|
||||
Handle::File(ref mut file) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle());
|
||||
|
||||
SocketT::handle_sendmsg(socket, file, payload, flags)
|
||||
}
|
||||
Handle::Null(_) => Err(SyscallError::new(syscall::EINVAL)),
|
||||
Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
// SocketCall::Unbind => self.handle_unbind(id),
|
||||
// SocketCall::GetToken => self.handle_get_token(id, payload),
|
||||
SocketCall::GetPeerName => {
|
||||
|
||||
@@ -3,7 +3,7 @@ use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::udp::{
|
||||
PacketBuffer as UdpSocketBuffer, PacketMetadata as UdpPacketMetadata, Socket as UdpSocket,
|
||||
};
|
||||
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
|
||||
use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint};
|
||||
use std::str;
|
||||
use syscall;
|
||||
use syscall::{Error as SyscallError, Result as SyscallResult};
|
||||
@@ -199,12 +199,13 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.data.is_specified() {
|
||||
return Err(SyscallError::new(syscall::EADDRNOTAVAIL));
|
||||
}
|
||||
if !file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
}
|
||||
// Unconnected sockets can only send via sendmsg/sendto.
|
||||
if !file.data.is_specified() {
|
||||
return Err(SyscallError::new(syscall::EDESTADDRREQ));
|
||||
}
|
||||
if self.can_send() {
|
||||
let endpoint = file.data;
|
||||
let endpoint = IpEndpoint::new(
|
||||
@@ -218,7 +219,57 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_sendmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
how: &[u8],
|
||||
_flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref sock_f) => sock_f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
if !socket_file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
}
|
||||
if self.can_send() {
|
||||
let usize_length = core::mem::size_of::<usize>();
|
||||
if how.len() < 3 * usize_length {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
let name_len = usize::from_le_bytes(
|
||||
how[0..usize_length].try_into().map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
let payload_len = usize::from_le_bytes(
|
||||
how[usize_length..2 * usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
// msg_controllen at [2*usize_length..3*usize_length], not used.
|
||||
let addr_start = 3 * usize_length;
|
||||
let payload_start = addr_start + name_len;
|
||||
if payload_start + payload_len > how.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
// Parse destination from the sendmsg address.
|
||||
let addr_str = core::str::from_utf8(&how[addr_start..addr_start + name_len])
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
|
||||
let dest = parse_endpoint(addr_str);
|
||||
let endpoint = IpEndpoint::new(
|
||||
dest.addr.unwrap_or(IpAddress::v4(0, 0, 0, 0)),
|
||||
dest.port,
|
||||
);
|
||||
self.send_slice(&how[payload_start..payload_start + payload_len], endpoint)
|
||||
.map_err(|_| SyscallError::new(syscall::EIO))?;
|
||||
Ok(payload_len)
|
||||
} else if socket_file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user