> From: Felipe Gasper <fel...@felipegasper.com>
> Sent: Thursday, 3 November, 2022 10:43
> >
> > And your description looks wrong anyway: shutdown(SHUT_RD) has
> > implementation-defined behavior for TCP sockets (because TCP does not
> > announce the read side of half-close to the peer), and on Linux causes
> > blocked receives and subsequent receives to return 0 (according to 
> > references
> 
> perl -MSocket -MIO::Socket::INET -e'my $s = IO::Socket::INET->new( Server =>
> 1, Listen => 1 ) or die; my $port = $s->sockport(); my $c = IO::Socket::INET-
> >new("localhost:$port") or die; syswrite $c, "hello"; my $sc = $s->accept();
> shutdown($sc, SHUT_RD); sysread $sc, my $buf, 512 or die $!; print $buf'
> 
> ^^ The above, I believe, demonstrates to the contrary: the read buffer is
> populated prior to shutdown and drained afterward.

As I noted, I hadn't tested it. The Linux man page is ambiguous:

   If how is SHUT_RD, further receptions will be disallowed.

It doesn't define "receptions". It's entirely possible that SHUT_RD will cause 
the stack to reject further application data (i.e. packets that increment the 
sequence number for anything other than ACK) from the peer, but permit the 
socket owner to continue to receive already-buffered data. That's arguably a 
poor implementation, and not what the man page appears to imply. And it looks 
to be in conflict with the Single UNIX Specification Issue 7 (not that Linux 
claims to be UNIX-conformant), which states that SHUT_SD "Disables further 
receive operations"; "operations" certainly seems to refer to actions taken by 
the caller, not by the peer.

There is a fair bit of debate about this online, and a number of people opine 
that the Linux behavior is correct, and SUS (they often refer to "POSIX", but 
POSIX has been superseded by SUS) is wrong. Others disagree.

The Linux kernel does take some action for a TCP socket that has SHUT_RD 
requested for it, but the behavior is not simple. (One SO comment mentions it 
causes it to exit the read loop in tcp_splice_read(), for example.) I'd be 
leery about relying on it.

I'm not sure how shutdown(SHUT_RD) is useful in the case of a TCP socket being 
used for TLS, to be perfectly honest. If the application protocol delimits 
messages properly and is half-duplex (request/response), then one side should 
know that no more data is expected and the other can detect incomplete 
messages, so there's likely no issue. If not, there's no way to guarantee you 
haven't encountered an incomplete message in bounded time (FPL Theorem 
applies). SHUT_RD does not signal the peer, so the peer can still get a RST if 
it continues to send. Perhaps I'm missing something, but I don't see what 
failure mode is being avoided by using SHUT_RD.

-- 
Michael Wojcik

Reply via email to