Consider the following situation, on Windows:
- listen socket 1 has the
SO_EXCLUSIVEADDRUSE
option set, and binds to a port - listen socket 1 receives an incoming connection, which it accepts to create a connected socket
- listen socket 1 is closed, but the connected socket remains open (in some sense, perhaps ESTABLISHED or TIME_WAIT)
- listen socket 2 has the
SO_EXCLUSIVEADDRUSE
option set, and attempts to bind to the same port as the first listen socket. Does it succeed?
There isn't a ton of information about this available on the web, but what there is mostly agrees that in the last step, bind
will always raise an error, because SO_EXCLUSIVEADDRUSE
prevents having listen socket 2 and the connected socket sharing the port. This is important because SO_EXCLUSIVEADDRUSE
provides some non-trivial security benefits, but if it breaks port rebinding then it's difficult to decide whether to use it or not.
The current MSDN docs are clear that in at least some cases a lingering connection can make the second bind
fail, though they're vague about what exactly counts as an "active connection":
Conversely, a socket with the SO_EXCLUSIVEADDRUSE set cannot necessarily be reused immediately after socket closure. For example, if a listening socket with SO_EXCLUSIVEADDRUSE set accepts a connection and is then subsequently closed, another socket (also with SO_EXCLUSIVEADDRUSE) cannot bind to the same port as the first socket until the original connection becomes inactive.
libuv explicitly chooses not to use SO_EXCLUSIVEADDRUSE
– despite the security benefits – because they say it interferes with TIME_WAIT
handling.
And this microsoft.com blog post from 2005 provides some more details, claiming that a connected socket can prevent SO_EXCLUSIVEADDRUSE
rebinding in at least some cases (though not TIME_WAIT
):
While the SO_EXCLUSIVEADDRUSE option is extremely useful, there is an important caveat. If at least one connection which originated from or was accepted on a port bound with exclusive access is active then all binds to this port will fail. In this case, a “connection” is defined as a socket which is explicitly connected to a peer via connect, WSAConnect, or ConnectEx with the exclusive flag set or a connection returned from a listening socket (such as from accept, WSAAccept, or AcceptEx) which has the exclusive option set (on the listening socket). An active port for TCP is defined as in the ESTABLISHED, FIN_WAIT, FIN_WAIT_2, or LAST_ACK states
So I wrote little script to try for myself:
https://gist.github.com/njsmith/8770bed5bbf2154940e8e3e7762e4ac3
and indeed, when I run it on Windows 10, I get:
rebind with existing listening socket: failed
details: OSError(10048, 'Only one usage of each socket address (protocol/network address/port) is normally permitted', None, 10048, None)
rebind with live connected sockets: succeeded
rebind with TIME_WAIT socket: succeeded
So my tentative conclusion is that MSDN and everyone else are wrong: so long as the original listening socket is closed, SO_EXCLUSIVEADDRUSE
actually does allow rebinding a port that has remaining connected sockets associated with it, whether in ESTABLISHED or TIME_WAIT state or what. Which is what everyone wants, so that's great. Presumably this was not true back in 2005, but sometime between then and now they fixed it.
Questions:
- Is my interpretation of these results correct?
- Is there another case I'm missing where a previous connection could cause a
SO_EXCLUSIVEADDRUSE
bind
to fail where a regularbind
would succeed? (I would expect that if ESTABLISHED sockets don't cause a problem, then nothing does, but I could be missing something.) - Most importantly: when did they make the change? For example, I tested on Windows 10, but if my code needed to target Windows Vista for some reason, would this work or not? Or did it always work like this and the docs were always wrong?