Fix error codes in UDP sendto and recvfrom

This commit is contained in:
Ruihan Li
2024-01-09 23:26:24 +08:00
committed by Tate, Hongliang Tian
parent 00cfdf86c8
commit 346887ace5
3 changed files with 109 additions and 31 deletions

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use smoltcp::socket::udp::{RecvError, SendError};
use crate::{ use crate::{
events::IoEvents, events::IoEvents,
@ -40,24 +41,39 @@ impl BoundDatagram {
buf: &mut [u8], buf: &mut [u8],
flags: SendRecvFlags, flags: SendRecvFlags,
) -> Result<(usize, IpEndpoint)> { ) -> Result<(usize, IpEndpoint)> {
self.bound_socket let result = self
.raw_with(|socket: &mut RawUdpSocket| socket.recv_slice(buf)) .bound_socket
.map_err(|_| Error::with_message(Errno::EAGAIN, "recv buf is empty")) .raw_with(|socket: &mut RawUdpSocket| socket.recv_slice(buf));
match result {
Ok((recv_len, endpoint)) => Ok((recv_len, endpoint)),
Err(RecvError::Exhausted) => {
return_errno_with_message!(Errno::EAGAIN, "the receive buffer is empty")
}
}
} }
pub fn try_sendto( pub fn try_sendto(
&self, &self,
buf: &[u8], buf: &[u8],
remote: Option<IpEndpoint>, remote: &IpEndpoint,
flags: SendRecvFlags, flags: SendRecvFlags,
) -> Result<usize> { ) -> Result<usize> {
let remote_endpoint = remote let result = self.bound_socket.raw_with(|socket: &mut RawUdpSocket| {
.or(self.remote_endpoint) if socket.payload_send_capacity() < buf.len() {
.ok_or_else(|| Error::with_message(Errno::EINVAL, "udp should provide remote addr"))?; return None;
self.bound_socket }
.raw_with(|socket: &mut RawUdpSocket| socket.send_slice(buf, remote_endpoint)) Some(socket.send_slice(buf, *remote))
.map(|_| buf.len()) });
.map_err(|_| Error::with_message(Errno::EAGAIN, "send udp packet fails")) match result {
Some(Ok(())) => Ok(buf.len()),
Some(Err(SendError::BufferFull)) => {
return_errno_with_message!(Errno::EAGAIN, "the send buffer is full")
}
Some(Err(SendError::Unaddressable)) => {
return_errno_with_message!(Errno::EINVAL, "the destionation address is invalid")
}
None => return_errno_with_message!(Errno::EMSGSIZE, "the message is too large"),
}
} }
pub(super) fn init_pollee(&self, pollee: &Pollee) { pub(super) fn init_pollee(&self, pollee: &Pollee) {

View File

@ -88,6 +88,15 @@ impl DatagramSocket {
self.nonblocking.store(nonblocking, Ordering::SeqCst); self.nonblocking.store(nonblocking, Ordering::SeqCst);
} }
fn remote_endpoint(&self) -> Option<IpEndpoint> {
let inner = self.inner.read();
match inner.as_ref() {
Inner::Bound(bound_datagram) => bound_datagram.remote_endpoint(),
Inner::Unbound(_) => None,
}
}
fn try_bind_empheral(&self, remote_endpoint: &IpEndpoint) -> Result<()> { fn try_bind_empheral(&self, remote_endpoint: &IpEndpoint) -> Result<()> {
// Fast path // Fast path
if let Inner::Bound(_) = self.inner.read().as_ref() { if let Inner::Bound(_) = self.inner.read().as_ref() {
@ -110,24 +119,23 @@ impl DatagramSocket {
fn try_recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> { fn try_recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> {
let inner = self.inner.read(); let inner = self.inner.read();
let Inner::Bound(bound_datagram) = inner.as_ref() else { let Inner::Bound(bound_datagram) = inner.as_ref() else {
return_errno_with_message!(Errno::EINVAL, "the socket is not bound"); return_errno_with_message!(Errno::EAGAIN, "the socket is not bound");
}; };
let (recv_bytes, remote_endpoint) = bound_datagram.try_recvfrom(buf, flags)?; let (recv_bytes, remote_endpoint) = bound_datagram.try_recvfrom(buf, flags)?;
bound_datagram.update_io_events(&self.pollee); bound_datagram.update_io_events(&self.pollee);
Ok((recv_bytes, remote_endpoint.into())) Ok((recv_bytes, remote_endpoint.into()))
} }
fn try_sendto( fn try_sendto(&self, buf: &[u8], remote: &IpEndpoint, flags: SendRecvFlags) -> Result<usize> {
&self,
buf: &[u8],
remote: Option<IpEndpoint>,
flags: SendRecvFlags,
) -> Result<usize> {
let inner = self.inner.read(); let inner = self.inner.read();
let Inner::Bound(bound_datagram) = inner.as_ref() else { let Inner::Bound(bound_datagram) = inner.as_ref() else {
return_errno_with_message!(Errno::EINVAL, "the socket is not bound"); return_errno_with_message!(Errno::EAGAIN, "the socket is not bound")
}; };
let sent_bytes = bound_datagram.try_sendto(buf, remote, flags)?; let sent_bytes = bound_datagram.try_sendto(buf, remote, flags)?;
bound_datagram.update_io_events(&self.pollee); bound_datagram.update_io_events(&self.pollee);
Ok(sent_bytes) Ok(sent_bytes)
@ -244,12 +252,7 @@ impl Socket for DatagramSocket {
} }
fn peer_addr(&self) -> Result<SocketAddr> { fn peer_addr(&self) -> Result<SocketAddr> {
let inner = self.inner.read(); self.remote_endpoint()
let Inner::Bound(bound_datagram) = inner.as_ref() else {
return_errno_with_message!(Errno::ENOTCONN, "the socket is not connected")
};
bound_datagram
.remote_endpoint()
.map(|endpoint| endpoint.into()) .map(|endpoint| endpoint.into())
.ok_or_else(|| Error::with_message(Errno::ENOTCONN, "the socket is not connected")) .ok_or_else(|| Error::with_message(Errno::ENOTCONN, "the socket is not connected"))
} }
@ -275,15 +278,21 @@ impl Socket for DatagramSocket {
debug_assert!(flags.is_all_supported()); debug_assert!(flags.is_all_supported());
let remote_endpoint = match remote { let remote_endpoint = match remote {
Some(remote_addr) => Some(remote_addr.try_into()?), Some(remote_addr) => {
None => None, let endpoint = remote_addr.try_into()?;
self.try_bind_empheral(&endpoint)?;
endpoint
}
None => self.remote_endpoint().ok_or_else(|| {
Error::with_message(
Errno::EDESTADDRREQ,
"the destination address is not specified",
)
})?,
}; };
if let Some(endpoint) = remote_endpoint {
self.try_bind_empheral(&endpoint)?;
}
// TODO: Block if the send buffer is full // TODO: Block if the send buffer is full
let sent_bytes = self.try_sendto(buf, remote_endpoint, flags)?; let sent_bytes = self.try_sendto(buf, &remote_endpoint, flags)?;
poll_ifaces(); poll_ifaces();
Ok(sent_bytes) Ok(sent_bytes)
} }

View File

@ -82,3 +82,56 @@ FN_TEST(getpeername)
addrlen == sizeof(saddr) && saddr.sin_port == C_PORT); addrlen == sizeof(saddr) && saddr.sin_port == C_PORT);
} }
END_TEST() END_TEST()
FN_TEST(send)
{
char buf[1] = { 'z' };
TEST_ERRNO(send(sk_unbound, buf, 1, 0), EDESTADDRREQ);
TEST_ERRNO(send(sk_bound, buf, 1, 0), EDESTADDRREQ);
}
END_TEST()
FN_TEST(recv)
{
char buf[1] = { 'z' };
TEST_ERRNO(recv(sk_unbound, buf, 1, 0), EAGAIN);
TEST_ERRNO(recv(sk_bound, buf, 1, 0), EAGAIN);
TEST_ERRNO(recv(sk_connected, buf, 1, 0), EAGAIN);
}
END_TEST()
FN_TEST(send_and_recv)
{
char buf[1];
struct sockaddr_in saddr;
socklen_t addrlen = sizeof(saddr);
sk_addr.sin_port = C_PORT;
buf[0] = 'a';
TEST_RES(sendto(sk_bound, buf, 1, 0, (struct sockaddr *)&sk_addr,
sizeof(sk_addr)),
_ret == 1);
buf[0] = 'b';
TEST_RES(send(sk_connected, buf, 1, 0), _ret == 1);
saddr.sin_port = 0;
buf[0] = 0;
TEST_RES(recvfrom(sk_bound, buf, 1, 0, (struct sockaddr *)&saddr,
&addrlen),
_ret == 1 && addrlen == sizeof(sk_addr) &&
saddr.sin_port == C_PORT && buf[0] == 'a');
saddr.sin_port = 0;
buf[0] = 0;
TEST_RES(recvfrom(sk_bound, buf, 1, 0, (struct sockaddr *)&saddr,
&addrlen),
_ret == 1 && addrlen == sizeof(sk_addr) &&
saddr.sin_port != C_PORT && buf[0] == 'b');
}
END_TEST()