Receive RST packets as ECONNRESET errors

This commit is contained in:
Ruihan Li
2025-02-24 23:07:58 +08:00
committed by Tate, Hongliang Tian
parent aa29640ed7
commit d40d452e9d
6 changed files with 256 additions and 31 deletions

View File

@ -10,8 +10,6 @@ pub enum BindError {
}
pub mod tcp {
pub use smoltcp::socket::tcp::{RecvError, SendError};
/// An error returned by [`TcpListener::new_listen`].
///
/// [`TcpListener::new_listen`]: crate::socket::TcpListener::new_listen
@ -51,6 +49,44 @@ pub mod tcp {
}
}
}
/// An error returned by [`TcpConnection::send`].
///
/// [`TcpConnection::send`]: crate::socket::TcpConnection::send
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SendError {
InvalidState,
/// The connection is reset.
ConnReset,
}
impl From<smoltcp::socket::tcp::SendError> for SendError {
fn from(value: smoltcp::socket::tcp::SendError) -> Self {
match value {
smoltcp::socket::tcp::SendError::InvalidState => Self::InvalidState,
}
}
}
/// An error returned by [`TcpConnection::recv`].
///
/// [`TcpConnection::recv`]: crate::socket::TcpConnection::recv
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RecvError {
InvalidState,
Finished,
/// The connection is reset.
ConnReset,
}
impl From<smoltcp::socket::tcp::RecvError> for RecvError {
fn from(value: smoltcp::socket::tcp::RecvError) -> Self {
match value {
smoltcp::socket::tcp::RecvError::InvalidState => Self::InvalidState,
smoltcp::socket::tcp::RecvError::Finished => Self::Finished,
}
}
}
}
pub mod udp {

View File

@ -17,7 +17,7 @@ use super::{
};
use crate::{
define_boolean_value,
errors::tcp::ConnectError,
errors::tcp::{ConnectError, RecvError, SendError},
ext::Ext,
iface::BoundPort,
socket::{
@ -42,6 +42,8 @@ pub struct RawTcpSocketExt<E: Ext> {
has_connected: bool,
/// Indicates if the receiving side of this socket is shut down by the user.
is_recv_shut: bool,
/// Indicates if the socket is closed by a RST packet.
is_rst_closed: bool,
}
impl<E: Ext> Deref for RawTcpSocketExt<E> {
@ -92,6 +94,14 @@ impl<E: Ext> RawTcpSocketExt<E> {
pub fn is_recv_shut(&self) -> bool {
self.is_recv_shut
}
/// Checks if the socket is closed by a RST packet.
///
/// Note that the flag is automatically cleared when it is read by
/// [`TcpConnection::clear_rst_closed`], [`TcpConnection::send`], or [`TcpConnection::recv`].
pub fn is_rst_closed(&self) -> bool {
self.is_rst_closed
}
}
define_boolean_value!(
@ -106,6 +116,7 @@ impl<E: Ext> RawTcpSocketExt<E> {
this: &Arc<TcpConnectionBg<E>>,
old_state: State,
old_recv_queue: usize,
is_rst: bool,
) -> (SocketEvents, TcpConnBecameDead) {
let became_dead = if self.state() != State::Established {
// After the connection is closed by the user, no new data can be read, and such unread
@ -117,6 +128,10 @@ impl<E: Ext> RawTcpSocketExt<E> {
&& matches!(old_state, State::FinWait1 | State::FinWait2)
&& self.recv_queue() > old_recv_queue
{
// Strictly speaking, the socket isn't closed by an incoming RST packet in this
// situation. Instead, we reset the connection and _send_ an outgoing RST packet.
// However, Linux reports `ECONNRESET`, so we have to follow Linux.
self.is_rst_closed = true;
self.abort();
}
self.check_dead(this)
@ -125,6 +140,9 @@ impl<E: Ext> RawTcpSocketExt<E> {
};
let events = if self.state() != old_state {
if self.state() == State::Closed && is_rst {
self.is_rst_closed = true;
}
self.on_new_state(this)
} else {
SocketEvents::empty()
@ -211,6 +229,7 @@ impl<E: Ext> TcpConnectionInner<E> {
listener,
has_connected: false,
is_recv_shut: false,
is_rst_closed: false,
};
TcpConnectionInner {
@ -344,7 +363,7 @@ impl<E: Ext> TcpConnection<E> {
/// Sends some data.
///
/// Polling the iface _may_ be required after this method succeeds.
pub fn send<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), smoltcp::socket::tcp::SendError>
pub fn send<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), SendError>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
@ -353,7 +372,12 @@ impl<E: Ext> TcpConnection<E> {
let mut socket = self.0.inner.lock();
if socket.is_rst_closed {
socket.is_rst_closed = false;
return Err(SendError::ConnReset);
}
let result = socket.send(f)?;
let need_poll = self
.0
.update_next_poll_at_ms(socket.poll_at(iface.context()));
@ -364,7 +388,7 @@ impl<E: Ext> TcpConnection<E> {
/// Receives some data.
///
/// Polling the iface _may_ be required after this method succeeds.
pub fn recv<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), smoltcp::socket::tcp::RecvError>
pub fn recv<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), RecvError>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
@ -374,10 +398,16 @@ impl<E: Ext> TcpConnection<E> {
let mut socket = self.0.inner.lock();
if socket.is_recv_shut && socket.recv_queue() == 0 {
return Err(smoltcp::socket::tcp::RecvError::Finished);
return Err(RecvError::Finished);
}
let result = match socket.recv(f) {
Err(_) if socket.is_rst_closed => {
socket.is_rst_closed = false;
return Err(RecvError::ConnReset);
}
res => res,
}?;
let result = socket.recv(f)?;
let need_poll = self
.0
.update_next_poll_at_ms(socket.poll_at(iface.context()));
@ -385,6 +415,18 @@ impl<E: Ext> TcpConnection<E> {
Ok((result, need_poll))
}
/// Checks if the socket is closed by a RST packet and clears the flag.
///
/// This flag is set when the socket is closed by a RST packet, and cleared when the connection
/// reset error is reported via one of the [`Self::send`], [`Self::recv`], or this method.
pub fn clear_rst_closed(&self) -> bool {
let mut socket = self.0.inner.lock();
let is_rst = socket.is_rst_closed;
socket.is_rst_closed = false;
is_rst
}
/// Shuts down the sending half of the connection.
///
/// This method will return `false` if the socket is in the CLOSED or TIME_WAIT state.
@ -534,6 +576,7 @@ impl<E: Ext> TcpConnectionBg<E> {
let old_state = socket.state();
let old_recv_queue = socket.recv_queue();
let is_rst = tcp_repr.control == TcpControl::Rst;
// For TCP, receiving an ACK packet can free up space in the queue, allowing more packets
// to be queued.
let mut events = SocketEvents::CAN_RECV | SocketEvents::CAN_SEND;
@ -543,7 +586,8 @@ impl<E: Ext> TcpConnectionBg<E> {
Some((ip_repr, tcp_repr)) => TcpProcessResult::ProcessedWithReply(ip_repr, tcp_repr),
};
let (state_events, became_dead) = socket.check_state(self, old_state, old_recv_queue);
let (state_events, became_dead) =
socket.check_state(self, old_state, old_recv_queue, is_rst);
events |= state_events;
self.add_events(events);
@ -565,6 +609,7 @@ impl<E: Ext> TcpConnectionBg<E> {
let old_state = socket.state();
let old_recv_queue = socket.recv_queue();
let mut is_rst = false;
let mut events = SocketEvents::empty();
let mut reply = None;
@ -581,11 +626,13 @@ impl<E: Ext> TcpConnectionBg<E> {
if !socket.accepts(cx, ip_repr, tcp_repr) {
break;
}
reply = socket.process(cx, ip_repr, tcp_repr);
is_rst |= tcp_repr.control == TcpControl::Rst;
events |= SocketEvents::CAN_RECV | SocketEvents::CAN_SEND;
reply = socket.process(cx, ip_repr, tcp_repr);
}
let (state_events, became_dead) = socket.check_state(self, old_state, old_recv_queue);
let (state_events, became_dead) =
socket.check_state(self, old_state, old_recv_queue, is_rst);
events |= state_events;
self.add_events(events);