From: Corinna Vinschen Date: Wed, 18 Jan 2006 18:24:33 +0000 (+0000) Subject: * autoload.cc (rresvport): Remove. X-Git-Tag: gdb-csl-20060226-branchpoint~140 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=06a5dd435ee5a226788943f0dd6de64baa3fc227;p=newlib-cygwin.git * autoload.cc (rresvport): Remove. * net.cc (last_used_rrecvport): New global shared variable. (cygwin_rresvport): Implement rresvport without using rresvport from wsock32. --- diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 44cde4438..7290af8d4 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,10 @@ +2006-01-18 Corinna Vinschen + + * autoload.cc (rresvport): Remove. + * net.cc (last_used_rrecvport): New global shared variable. + (cygwin_rresvport): Implement rresvport without using rresvport from + wsock32. + 2006-01-18 Corinna Vinschen * include/cygwin/socket.h (struct sockaddr_storage): Fix typo in diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 76f38a3b2..0d76ebeae 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -450,7 +450,6 @@ LoadDLLfunc (SetUserObjectSecurity, 12, user32) LoadDLLfunc (inet_network, 4, wsock32) LoadDLLfunc (rcmd, 24, wsock32) LoadDLLfunc (rexec, 24, wsock32) -LoadDLLfunc (rresvport, 4, wsock32) LoadDLLfunc (accept, 12, ws2_32) LoadDLLfunc (bind, 12, ws2_32) diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 1b3e4aca3..3ec3cc1dc 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -18,6 +18,7 @@ details. */ #include #include #include +#include #include #define gethostname cygwin_gethostname @@ -1834,6 +1835,32 @@ cygwin_rcmd (char **ahost, unsigned short inport, char *locuser, return res; } +/* The below implementation of rresvport looks pretty ugly, but there's + a problem in Winsock. The bind(2) call does not fail if a local + address is still in TIME_WAIT state, and there's no way to get this + behaviour. Unfortunately the first time when this is detected is when + the calling application tries to connect. + + One (also not really foolproof) way around this problem would be to use + the iphlpapi function GetTcpTable and to check if the port in question is + in TIME_WAIT state and if so, choose another port number. But this method + is as prone to races as the below one, or any other method using random + port numbers, etc. The below method at least tries to avoid races between + multiple applications using rrecvport. + + As for the question "why don't you just use the Winsock rresvport?"... + For some reason I do NOT understand, the call to WinSocks rresvport + corrupts the stack when Cygwin is built using -fomit-frame-pointers. + And then again, the Winsock rresvport function has the exact same + problem with reusing ports in the TIME_WAIT state as the socket/bind + method has. So there's no gain in using that function. */ + +#define PORT_LOW (IPPORT_EFSSERVER + 1) +#define PORT_HIGH (IPPORT_RESERVED - 1) +#define NUM_PORTS (PORT_HIGH - PORT_LOW + 1) + +LONG last_used_rrecvport __attribute__((section (".cygwin_dll_common"), shared)) = IPPORT_RESERVED; + /* exported as rresvport: standards? */ extern "C" int cygwin_rresvport (int *port) @@ -1845,7 +1872,38 @@ cygwin_rresvport (int *port) if (efault.faulted (EFAULT)) return -1; - res = rresvport (port); + res = socket (AF_INET, SOCK_STREAM, 0); + if (res != (int) INVALID_SOCKET) + { + LONG myport; + int ret = SOCKET_ERROR; + struct sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + + for (int i = 0; i < NUM_PORTS; i++) + { + while ((myport = InterlockedExchange (&last_used_rrecvport, 0)) == 0) + low_priority_sleep (0); + if (--myport < PORT_LOW) + myport = PORT_HIGH; + InterlockedExchange (&last_used_rrecvport, myport); + + sin.sin_port = htons (myport); + if (!(ret = bind (res, (struct sockaddr *) &sin, sizeof sin))) + break; + int err = WSAGetLastError (); + if (err != WSAEADDRINUSE && err != WSAEINVAL) + break; + } + if (ret == SOCKET_ERROR) + { + closesocket (res); + res = (int) INVALID_SOCKET; + } + else if (port) + *port = myport; + } if (res != (int) INVALID_SOCKET) {