diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index cf4ed09bb5..d084c95cd8 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -44,6 +44,63 @@ impl<'a> SchemeSocket for TcpSocket<'a> { self.can_send() } + fn call( + &mut self, + _file: &mut SchemeFile, + payload: &mut [u8], + metadata: &[u64], + _ctx: &CallerCtx, + ) -> SyscallResult { + match SocketCall::from(metadata[0]) { + SocketCall::SendMsg => { + let flags = metadata[1] as u16; + // smoltcp's TCP socket has no MSG_NOSIGNAL awareness: a + // peer-closed connection returns EPIPE on the send path + // and the kernel delivers SIGPIPE to the calling thread. + // We emulate POSIX MSG_NOSIGNAL by suppressing SIGPIPE + // for the duration of this send only. The block is + // restored before returning, so any newly-pending SIGPIPE + // (delivered between the signal mask save and restore) is + // dequeued via sigtimedwait(..., NULL) before we resume + // the caller's signal mask. + if flags & libc::MSG_NOSIGNAL as u16 != 0 { + let mut old_mask: libc::sigset_t = unsafe { std::mem::zeroed() }; + let mut block_mask: libc::sigset_t = unsafe { std::mem::zeroed() }; + unsafe { libc::sigemptyset(&mut block_mask) }; + unsafe { libc::sigaddset(&mut block_mask, libc::SIGPIPE) }; + if unsafe { libc::pthread_sigmask(libc::SIG_BLOCK, &block_mask, &mut old_mask) } == 0 { + let written = self.send_slice(payload); + if written < 0 { + self.close(); + let mut ts: libc::timespec = + unsafe { std::mem::zeroed() }; + ts.tv_sec = 0; + ts.tv_nsec = 0; + unsafe { + let _ = libc::sigtimedwait( + &block_mask, + std::ptr::null_mut(), + &ts, + ); + } + } + unsafe { + libc::pthread_sigmask(libc::SIG_SETMASK, &old_mask, std::ptr::null_mut()); + } + Ok(written.max(0) as usize) + } else { + // pthread_sigmask failed; fall through to plain + // send without suppression. + Ok(self.send_slice(payload).max(0) as usize) + } + } else { + Ok(self.send_slice(payload).max(0) as usize) + } + } + _ => Err(SyscallError::new(syscall::EOPNOTSUPP)), + } + } + fn can_recv(&mut self, _data: &Self::DataT) -> bool { smoltcp::socket::tcp::Socket::can_recv(self) }