[openssl-users] Close TCP socket after SSL_clear()?

Michael Wojcik Michael.Wojcik at microfocus.com
Sat Jan 12 14:20:21 UTC 2019

> From: openssl-users [mailto:openssl-users-bounces at openssl.org] On Behalf Of
> Charles Mills
> Sent: Friday, January 11, 2019 17:06
> >        SSL_shutdown(connection) || SSL_shutdown(connection);
> I like it! (Not!)
> I don't pretend to be a bits and bytes expert on TCP protocol. You can't be
> an expert on everything.
> So I will listen to expert advice. I know 99% of you all are 'nix guys and
> this is a Windows problem. I am seeing OTOH where my Windows doc says
> closesocket() does an abortive termination, and OTOH a discussion of a
> graceful closesocket() with SO_LINGER/SO_DONTLINGER.
> (1) This code is (at the application level) purely a receiver of data and
> (2) without the TLS layer in place it is hard to picture any meaningful data
> transfer and (3) we are in a session cleanup situation anyway -- so it seems
> to me that an abortive disconnect is perfectly fine. Am I wrong?

Yes, you're wrong. You don't want an abortive disconnect.

A TCP connection can be closed in four (or five) ways:

1. Normal close, which involves the FIN / FIN-ACK / ACK sequence. When the last ACK is received, both sides know that all data has been received by the peer stack, and at the point when the corresponding ACK was generated, the peer "believed" it would be able to deliver the data to the application eventually. (That is, the stack hadn't been informed that the application's identifier for the connection - the socket - had been closed.)

2. Abortive close, which involves a RST from one side to the peer, and that's it. RST is a one-way, unacknowledged flow. There are a number of reasons why it's undesirable, some of which I'll go into below.

3. Abortive close due to network management message: the stack receives an ICMP message indicating a packet could not be delivered, such as HOST_UNREACH. From the application's point of view, the result is similar to #2, except for the particular error code it sees.

4. Timeout from TCP retransmit, either for an application send or, if it's enabled, TCP keepalive.

5. Arguably a separate case: 1-3 but generated by a middlebox, such as as a router, or an application firewall. In other words, the connection is forced closed by someone spoofing the peer. From the application's point of view, that makes no difference.

Applications should almost never use an abortive close. TCP is intended to be a reliable (best-effort) stream transport, and it can only meet its (already weak) service guarantees if you let it acknowledge all application data and close the conversation cleanly.

Now, when you have a higher-level conversation protocol such as TLS, and the higher-level protocol has already negotiated end-of-conversation, that may not seem important; the peers have agreed that they're not going to send anything more. That assumes, however, that the peers are well-behaved. And it is at the very least notionally cleaner to let the conversation close normally.

Beyond that, an abortive close can cause TIME_WAIT Assassination, which is a Bad Thing. If you don't know what TIME_WAIT Assassination is, that's a sign you shouldn't be doing abortive closes. Don't invoke extraordinary behavior you don't understand.

Now, all that said: Winsock closesocket will NOT do an abortive disconnect if you have not mucked with the SO_LINGER socket option (which you should not do unless you understand TCP). I don't know what documentation you saw that claims otherwise, but it's wrong.

Calling shutdown before closesocket won't hurt anything, but (if you use the pattern that we've discussed in this thread) won't do anything useful either, in most cases.

One case I forgot in my previous discussion: It's worth remembering that close/closesocket operates on a single reference to the connection, while shutdown operates on the connection itself. That is, the logic for close/closesocket is notionally something like this:

   close the descriptor/handle
   decrement the conversation's reference count
   if the reference count is 0
      if connection is still open for sending
      if connection is still open for receiving

In the case where you have multiple descriptors or handles for a conversation - for example due to dup'ing a socket or forking on UNIX, or duplicating a handle (possibly into a different process) on Windows - then close/closesocket won't do the shutdown-equivalent until they have *all* been closed. An explicit shutdown, on the other hand, doesn't close any of the descriptors/handles, but it does send a FIN (for SHUT_WR) or flush inbound data and refuse to receive any more (for SHUT_RD) on the conversation, which of course affects all descriptors/handles.

So if your application creates multiple references to the conversation, then depending on your design, you might want the shutdown. Or you might not, if you want the shutdown-on-last-close semantics. Neither option is correct for all applications.

Michael Wojcik
Distinguished Engineer, Micro Focus

More information about the openssl-users mailing list