* 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:
		| @@ -501,6 +501,7 @@ LoadDLLfuncEx (GetIfTable, 12, iphlpapi, 1) | |||||||
| LoadDLLfuncEx (GetIfEntry, 4, iphlpapi, 1) | LoadDLLfuncEx (GetIfEntry, 4, iphlpapi, 1) | ||||||
| LoadDLLfuncEx (GetIpAddrTable, 12, iphlpapi, 1) | LoadDLLfuncEx (GetIpAddrTable, 12, iphlpapi, 1) | ||||||
| LoadDLLfuncEx (GetNetworkParams, 8, iphlpapi, 1) | LoadDLLfuncEx (GetNetworkParams, 8, iphlpapi, 1) | ||||||
|  | LoadDLLfuncEx (GetTcpTable, 12, iphlpapi, 1) | ||||||
|  |  | ||||||
| LoadDLLfunc (CoTaskMemFree, 4, ole32) | LoadDLLfunc (CoTaskMemFree, 4, ole32) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ | |||||||
| #include <sys/un.h> | #include <sys/un.h> | ||||||
| #include <sys/uio.h> | #include <sys/uio.h> | ||||||
| #include <asm/byteorder.h> | #include <asm/byteorder.h> | ||||||
|  | #include <iphlpapi.h> | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #define USE_SYS_TYPES_FD_SET | #define USE_SYS_TYPES_FD_SET | ||||||
| @@ -580,6 +581,29 @@ fhandler_socket::link (const char *newpath) | |||||||
|   return fhandler_base::link (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 | int | ||||||
| fhandler_socket::bind (const struct sockaddr *name, int namelen) | 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); | 	      debug_printf ("%d = setsockopt (SO_EXCLUSIVEADDRUSE), %E", ret); | ||||||
| 	    } | 	    } | ||||||
| 	  else | 	  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)) |       if (::bind (get_socket (), name, namelen)) | ||||||
| 	set_winsock_errno (); | 	set_winsock_errno (); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user