* autoload.cc (GetTcpTable): Define.

* fhandler_socket.cc (address_in_use): New function to check if
	sockaddr_in address is already in use.
	(fhandler_socket::bind): Check if address is alreay in use in case of
	SO_REUSEADDR, to circumvent WinSock non-standard behaviour.
This commit is contained in:
Corinna Vinschen 2006-02-01 11:10:53 +00:00
parent 0ca697dde1
commit 0ee535ccb1
2 changed files with 43 additions and 1 deletions

View File

@ -501,6 +501,7 @@ LoadDLLfuncEx (GetIfTable, 12, iphlpapi, 1)
LoadDLLfuncEx (GetIfEntry, 4, iphlpapi, 1)
LoadDLLfuncEx (GetIpAddrTable, 12, iphlpapi, 1)
LoadDLLfuncEx (GetNetworkParams, 8, iphlpapi, 1)
LoadDLLfuncEx (GetTcpTable, 12, iphlpapi, 1)
LoadDLLfunc (CoTaskMemFree, 4, ole32)

View File

@ -17,6 +17,7 @@
#include <sys/un.h>
#include <sys/uio.h>
#include <asm/byteorder.h>
#include <iphlpapi.h>
#include <stdlib.h>
#define USE_SYS_TYPES_FD_SET
@ -580,6 +581,29 @@ fhandler_socket::link (const char *newpath)
return fhandler_base::link (newpath);
}
static inline bool
address_in_use (struct sockaddr_in *addr)
{
PMIB_TCPTABLE tab;
PMIB_TCPROW entry;
DWORD size = 0, i;
if (GetTcpTable (NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
{
tab = (PMIB_TCPTABLE) alloca (size);
if (!GetTcpTable (tab, &size, FALSE))
{
for (i = tab->dwNumEntries, entry = tab->table; i > 0; --i, ++entry)
if (entry->dwLocalAddr == addr->sin_addr.s_addr
&& entry->dwLocalPort == addr->sin_port
&& entry->dwState >= MIB_TCP_STATE_LISTEN
&& entry->dwState <= MIB_TCP_STATE_LAST_ACK)
return true;
}
}
return false;
}
int
fhandler_socket::bind (const struct sockaddr *name, int namelen)
{
@ -681,7 +705,24 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
debug_printf ("%d = setsockopt (SO_EXCLUSIVEADDRUSE), %E", ret);
}
else
debug_printf ("SO_REUSEADDR set");
{
debug_printf ("SO_REUSEADDR set");
/* There's a bug in SO_REUSEADDR handling in WinSock.
Per standards, we must not be able to reuse a complete
duplicate of a local TCP address (same IP, same port),
even if SO_REUSEADDR has been set. That's unfortunately
possible in WinSock. So we're testing here if the local
address is already in use and don't bind, if so. This
only works for OSes with IP Helper support. */
if (get_socket_type () == SOCK_STREAM
&& wincap.has_ip_helper_lib ()
&& address_in_use ((struct sockaddr_in *) name))
{
debug_printf ("Local address in use, don't bind");
set_errno (EADDRINUSE);
goto out;
}
}
}
if (::bind (get_socket (), name, namelen))
set_winsock_errno ();