* autoload.cc (rresvport): Remove.

* net.cc (last_used_rrecvport): New global shared variable.
	(cygwin_rresvport): Implement rresvport without using rresvport from
	wsock32.
This commit is contained in:
Corinna Vinschen 2006-01-18 18:24:33 +00:00
parent 25ef7a93b6
commit 06a5dd435e
3 changed files with 66 additions and 2 deletions

View File

@ -1,3 +1,10 @@
2006-01-18 Corinna Vinschen <corinna@vinschen.de>
* 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 <corinna@vinschen.de>
* include/cygwin/socket.h (struct sockaddr_storage): Fix typo in

View File

@ -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)

View File

@ -18,6 +18,7 @@ details. */
#include <sys/socket.h>
#include <sys/un.h>
#include <iphlpapi.h>
#include <syslog.h>
#include <stdlib.h>
#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)
{