diff options
author | max <max@tclers.tk> | 2016-10-04 13:31:49 (GMT) |
---|---|---|
committer | max <max@tclers.tk> | 2016-10-04 13:31:49 (GMT) |
commit | 5b377a6192b167ec34c088deab0eed8aa80253bb (patch) | |
tree | e2e9a09cddae7e484fc3231fde14048f5b78a3e3 /unix | |
parent | 2735ae8a8f3cd9a14778858d17f195bf54fc3303 (diff) | |
download | tcl-5b377a6192b167ec34c088deab0eed8aa80253bb.zip tcl-5b377a6192b167ec34c088deab0eed8aa80253bb.tar.gz tcl-5b377a6192b167ec34c088deab0eed8aa80253bb.tar.bz2 |
When opening a server socket on an ephemeral port, make sure that the
port number that gets picked for IPv4 is also available on IPv6. If
not, start all over for up to ten times to find a port number that is
available on both protocols.
Diffstat (limited to 'unix')
-rw-r--r-- | unix/tclUnixSock.c | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 8167077..f7bd0be 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -1429,7 +1429,7 @@ Tcl_OpenTcpServer( * clients. */ ClientData acceptProcData) /* Data for the callback. */ { - int status = 0, sock = -1, reuseaddr = 1, chosenport = 0; + int status = 0, sock = -1, reuseaddr = 1, chosenport; struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */ TcpState *statePtr = NULL; char channelName[SOCK_CHAN_LENGTH]; @@ -1444,6 +1444,37 @@ Tcl_OpenTcpServer( enum { LOOKUP, SOCKET, BIND, LISTEN } howfar = LOOKUP; int my_errno = 0; + /* + * If we were called with port 0 to listen on a random port number, we + * copy the port number from the first member of the addrinfo list to all + * subsequent members, so that IPv4 and IPv6 listen on the same port. This + * might fail to bind() with EADDRINUSE if a port is free on the first + * address family in the list but already used on the other. In this case + * we revert everything we've done so far and start from scratch hoping + * that next time we'll find a port number that is usable on all address + * families. We try this at most MAXRETRY times to avoid an endless loop + * if all ports are taken. + */ + int retry = 0; +#define MAXRETRY 10 + + repeat: + if (retry > 0) { + if (statePtr != NULL) { + TcpCloseProc(statePtr, NULL); + statePtr = NULL; + } + if (addrlist != NULL) { + freeaddrinfo(addrlist); + addrlist = NULL; + } + if (retry >= MAXRETRY) { + goto error; + } + } + retry++; + chosenport = 0; + if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) { my_errno = errno; goto error; @@ -1512,6 +1543,9 @@ Tcl_OpenTcpServer( } close(sock); sock = -1; + if (port == 0 && errno == EADDRINUSE) { + goto repeat; + } continue; } if (port == 0 && chosenport == 0) { @@ -1535,6 +1569,9 @@ Tcl_OpenTcpServer( } close(sock); sock = -1; + if (port == 0 && errno == EADDRINUSE) { + goto repeat; + } continue; } if (statePtr == NULL) { |