Set socket errors after connecting

This commit is contained in:
Ruihan Li
2024-03-20 23:46:51 +08:00
committed by Tate, Hongliang Tian
parent 27c5c27fd0
commit 698e748150
2 changed files with 55 additions and 15 deletions

View File

@ -204,6 +204,8 @@ impl StreamSocket {
} }
fn check_connect(&self) -> Result<()> { fn check_connect(&self) -> Result<()> {
// Hold the lock in advance to avoid deadlocks.
let mut options = self.options.write();
let mut state = self.state.write(); let mut state = self.state.write();
match state.as_mut() { match state.as_mut() {
@ -212,8 +214,9 @@ impl StreamSocket {
} }
State::Connected(connected_stream) => connected_stream.check_new(), State::Connected(connected_stream) => connected_stream.check_new(),
State::Init(_) | State::Listen(_) => { State::Init(_) | State::Listen(_) => {
// FIXME: The error code should be retrieved via the `SO_ERROR` socket option let sock_errors = options.socket.sock_errors();
return_errno_with_message!(Errno::ECONNREFUSED, "the connection is refused"); options.socket.set_sock_errors(None);
sock_errors.map(Err).unwrap_or(Ok(()))
} }
} }
} }
@ -518,13 +521,24 @@ impl Socket for StreamSocket {
} }
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> { fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
let options = self.options.read(); // Note that the socket error has to be handled separately, because it is automatically
// cleared after reading.
match_sock_option_mut!(option, { match_sock_option_mut!(option, {
// Socket Options
socket_errors: SocketError => { socket_errors: SocketError => {
let mut options = self.options.write();
let sock_errors = options.socket.sock_errors(); let sock_errors = options.socket.sock_errors();
socket_errors.set(sock_errors); socket_errors.set(sock_errors);
options.socket.set_sock_errors(None);
return Ok(());
}, },
_ => ()
});
let options = self.options.read();
match_sock_option_mut!(option, {
// Socket options:
socket_reuse_addr: ReuseAddr => { socket_reuse_addr: ReuseAddr => {
let reuse_addr = options.socket.reuse_addr(); let reuse_addr = options.socket.reuse_addr();
socket_reuse_addr.set(reuse_addr); socket_reuse_addr.set(reuse_addr);
@ -541,7 +555,7 @@ impl Socket for StreamSocket {
let reuse_port = options.socket.reuse_port(); let reuse_port = options.socket.reuse_port();
socket_reuse_port.set(reuse_port); socket_reuse_port.set(reuse_port);
}, },
// Tcp Options // TCP options:
tcp_no_delay: NoDelay => { tcp_no_delay: NoDelay => {
let no_delay = options.tcp.no_delay(); let no_delay = options.tcp.no_delay();
tcp_no_delay.set(no_delay); tcp_no_delay.set(no_delay);
@ -565,17 +579,19 @@ impl Socket for StreamSocket {
let window_clamp = options.tcp.window_clamp(); let window_clamp = options.tcp.window_clamp();
tcp_window_clamp.set(window_clamp); tcp_window_clamp.set(window_clamp);
}, },
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "get unknown option") _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown")
}); });
Ok(()) Ok(())
} }
fn set_option(&self, option: &dyn SocketOption) -> Result<()> { fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
let mut options = self.options.write(); let mut options = self.options.write();
// FIXME: here we have only set the value of the option, without actually // FIXME: here we have only set the value of the option, without actually
// making any real modifications. // making any real modifications.
match_sock_option_ref!(option, { match_sock_option_ref!(option, {
// Socket options // Socket options:
socket_recv_buf: RecvBuf => { socket_recv_buf: RecvBuf => {
let recv_buf = socket_recv_buf.get().unwrap(); let recv_buf = socket_recv_buf.get().unwrap();
if *recv_buf <= MIN_RECVBUF { if *recv_buf <= MIN_RECVBUF {
@ -604,7 +620,7 @@ impl Socket for StreamSocket {
let linger = socket_linger.get().unwrap(); let linger = socket_linger.get().unwrap();
options.socket.set_linger(*linger); options.socket.set_linger(*linger);
}, },
// Tcp options // TCP options:
tcp_no_delay: NoDelay => { tcp_no_delay: NoDelay => {
let no_delay = tcp_no_delay.get().unwrap(); let no_delay = tcp_no_delay.get().unwrap();
options.tcp.set_no_delay(*no_delay); options.tcp.set_no_delay(*no_delay);
@ -616,12 +632,11 @@ impl Socket for StreamSocket {
tcp_maxseg: MaxSegment => { tcp_maxseg: MaxSegment => {
const MIN_MAXSEG: u32 = 536; const MIN_MAXSEG: u32 = 536;
const MAX_MAXSEG: u32 = 65535; const MAX_MAXSEG: u32 = 65535;
let maxseg = tcp_maxseg.get().unwrap(); let maxseg = tcp_maxseg.get().unwrap();
if *maxseg < MIN_MAXSEG || *maxseg > MAX_MAXSEG { if *maxseg < MIN_MAXSEG || *maxseg > MAX_MAXSEG {
return_errno_with_message!(Errno::EINVAL, "New maxseg should be in allowed range."); return_errno_with_message!(Errno::EINVAL, "the maximum segment size is out of bounds");
} }
options.tcp.set_maxseg(*maxseg); options.tcp.set_maxseg(*maxseg);
}, },
tcp_window_clamp: WindowClamp => { tcp_window_clamp: WindowClamp => {
@ -633,8 +648,9 @@ impl Socket for StreamSocket {
options.tcp.set_window_clamp(*window_clamp); options.tcp.set_window_clamp(*window_clamp);
} }
}, },
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "set unknown option") _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
}); });
Ok(()) Ok(())
} }
} }
@ -644,9 +660,11 @@ impl Observer<()> for StreamSocket {
let conn_ready = self.update_io_events(); let conn_ready = self.update_io_events();
if conn_ready { if conn_ready {
// FIXME: The error code should be stored as the `SO_ERROR` socket option. Since it // Hold the lock in advance to avoid race conditions.
// does not exist yet, we ignore the return value below. let mut options = self.options.write();
let _ = self.finish_connect();
let result = self.finish_connect();
options.socket.set_sock_errors(result.err());
} }
} }
} }

View File

@ -268,3 +268,25 @@ FN_TEST(connect)
TEST_ERRNO(connect(sk_accepted, psaddr, addrlen), EISCONN); TEST_ERRNO(connect(sk_accepted, psaddr, addrlen), EISCONN);
} }
END_TEST() END_TEST()
FN_TEST(async_connect)
{
struct pollfd pfd = { .fd = sk_bound, .events = POLLOUT };
int err;
socklen_t errlen = sizeof(err);
sk_addr.sin_port = 0xbeef;
TEST_ERRNO(connect(sk_bound, (struct sockaddr *)&sk_addr,
sizeof(sk_addr)),
EINPROGRESS);
TEST_RES(poll(&pfd, 1, 60), pfd.revents & POLLOUT);
TEST_RES(getsockopt(sk_bound, SOL_SOCKET, SO_ERROR, &err, &errlen),
errlen == sizeof(err) && err == ECONNREFUSED);
// Reading the socket error will cause it to be cleared
TEST_RES(getsockopt(sk_bound, SOL_SOCKET, SO_ERROR, &err, &errlen),
errlen == sizeof(err) && err == 0);
}
END_TEST()