From dfadd177a9738de289a3a6d4b39c08d94f384c1a Mon Sep 17 00:00:00 2001 From: Denis Smirnov Date: Thu, 5 Oct 2023 16:04:28 +0700 Subject: [PATCH] Make TCP connect() handle EINTR correctly According to the POSIX standard, if connect() is interrupted by a signal that is caught while blocked waiting to establish a connection, connect() shall fail and set errno to EINTR, but the connection request shall not be aborted, and the connection shall be established asynchronously. If asynchronous connection was successfully established after EINTR and before the next connection attempt, OS returns EISCONN that was handled as an error before. This behavior is fixed now and we handle it as success. The problem affects MacOS users: Linux doesn't return EISCONN in this case, Windows connect() can not be interrupted without an old-fashoin WSACancelBlockingCall function that is not used in the library. So current solution gives connect() as OS specific implementation. --- library/std/src/sys/hermit/net.rs | 6 ++++++ library/std/src/sys/solid/net.rs | 11 +++++++---- library/std/src/sys/unix/net.rs | 17 +++++++++++++++++ library/std/src/sys/windows/net.rs | 12 +++++++----- library/std/src/sys_common/net.rs | 4 +--- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index a564f1698ba..bd8b493d65a 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -56,6 +56,12 @@ pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { unimplemented!() } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; let r = unsafe { diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 6adced787f3..1eae0fc0642 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -233,12 +233,15 @@ pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { } } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt(unsafe { netc::connect(self.0.raw(), addr.as_ptr(), len) })?; + Ok(()) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(netc::connect(self.0.raw(), addr.as_ptr(), len)) - }; + let r = self.connect(addr); self.set_nonblocking(false)?; match r { diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index f450d708dae..5c4d776e18a 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -6,6 +6,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::str; use crate::sys::fd::FileDesc; +use crate::sys::unix::IsMinusOne; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -140,6 +141,22 @@ pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { unimplemented!() } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + loop { + let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; + if result.is_minus_one() { + let err = crate::sys::os::errno(); + match err { + libc::EINTR => continue, + libc::EISCONN => return Ok(()), + _ => return Err(io::Error::from_raw_os_error(err)), + } + } + return Ok(()); + } + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; let r = unsafe { diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 4b7115f97c5..c29b863665f 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -140,13 +140,15 @@ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { } } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; + cvt(result).map(drop) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; - let result = { - let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; - cvt(result).map(drop) - }; + let result = self.connect(addr); self.set_nonblocking(false)?; match result { diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 4f5b17deaa2..8712bd2eca7 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -226,9 +226,7 @@ pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; - - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?; + sock.connect(addr)?; Ok(TcpStream { inner: sock }) }